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)
2682 this.headerContainerEl.dom.innerHTML = html;
2691 * Card header - holder for the card header elements.
2696 * @class Roo.bootstrap.CardHeader
2697 * @extends Roo.bootstrap.Element
2698 * Bootstrap CardHeader class
2700 * Create a new Card Header - that you can embed children into
2701 * @param {Object} config The config object
2704 Roo.bootstrap.CardHeader = function(config){
2705 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2711 container_method : 'getCardHeader'
2724 * Card footer - holder for the card footer elements.
2729 * @class Roo.bootstrap.CardFooter
2730 * @extends Roo.bootstrap.Element
2731 * Bootstrap CardFooter class
2733 * Create a new Card Footer - that you can embed children into
2734 * @param {Object} config The config object
2737 Roo.bootstrap.CardFooter = function(config){
2738 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2744 container_method : 'getCardFooter'
2757 * Card header - holder for the card header elements.
2762 * @class Roo.bootstrap.CardImageTop
2763 * @extends Roo.bootstrap.Element
2764 * Bootstrap CardImageTop class
2766 * Create a new Card Image Top container
2767 * @param {Object} config The config object
2770 Roo.bootstrap.CardImageTop = function(config){
2771 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2777 container_method : 'getCardImageTop'
2795 * @class Roo.bootstrap.Img
2796 * @extends Roo.bootstrap.Component
2797 * Bootstrap Img class
2798 * @cfg {Boolean} imgResponsive false | true
2799 * @cfg {String} border rounded | circle | thumbnail
2800 * @cfg {String} src image source
2801 * @cfg {String} alt image alternative text
2802 * @cfg {String} href a tag href
2803 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804 * @cfg {String} xsUrl xs image source
2805 * @cfg {String} smUrl sm image source
2806 * @cfg {String} mdUrl md image source
2807 * @cfg {String} lgUrl lg image source
2810 * Create a new Input
2811 * @param {Object} config The config object
2814 Roo.bootstrap.Img = function(config){
2815 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2821 * The img click event for the img.
2822 * @param {Roo.EventObject} e
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2830 imgResponsive: true,
2840 getAutoCreate : function()
2842 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843 return this.createSingleImg();
2848 cls: 'roo-image-responsive-group',
2853 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2855 if(!_this[size + 'Url']){
2861 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862 html: _this.html || cfg.html,
2863 src: _this[size + 'Url']
2866 img.cls += ' roo-image-responsive-' + size;
2868 var s = ['xs', 'sm', 'md', 'lg'];
2870 s.splice(s.indexOf(size), 1);
2872 Roo.each(s, function(ss){
2873 img.cls += ' hidden-' + ss;
2876 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877 cfg.cls += ' img-' + _this.border;
2881 cfg.alt = _this.alt;
2894 a.target = _this.target;
2898 cfg.cn.push((_this.href) ? a : img);
2905 createSingleImg : function()
2909 cls: (this.imgResponsive) ? 'img-responsive' : '',
2911 src : 'about:blank' // just incase src get's set to undefined?!?
2914 cfg.html = this.html || cfg.html;
2916 cfg.src = this.src || cfg.src;
2918 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919 cfg.cls += ' img-' + this.border;
2936 a.target = this.target;
2941 return (this.href) ? a : cfg;
2944 initEvents: function()
2947 this.el.on('click', this.onClick, this);
2952 onClick : function(e)
2954 Roo.log('img onclick');
2955 this.fireEvent('click', this, e);
2958 * Sets the url of the image - used to update it
2959 * @param {String} url the url of the image
2962 setSrc : function(url)
2966 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967 this.el.dom.src = url;
2971 this.el.select('img', true).first().dom.src = url;
2987 * @class Roo.bootstrap.Link
2988 * @extends Roo.bootstrap.Component
2989 * Bootstrap Link Class
2990 * @cfg {String} alt image alternative text
2991 * @cfg {String} href a tag href
2992 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993 * @cfg {String} html the content of the link.
2994 * @cfg {String} anchor name for the anchor link
2995 * @cfg {String} fa - favicon
2997 * @cfg {Boolean} preventDefault (true | false) default false
3001 * Create a new Input
3002 * @param {Object} config The config object
3005 Roo.bootstrap.Link = function(config){
3006 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3012 * The img click event for the img.
3013 * @param {Roo.EventObject} e
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3023 preventDefault: false,
3029 getAutoCreate : function()
3031 var html = this.html || '';
3033 if (this.fa !== false) {
3034 html = '<i class="fa fa-' + this.fa + '"></i>';
3039 // anchor's do not require html/href...
3040 if (this.anchor === false) {
3042 cfg.href = this.href || '#';
3044 cfg.name = this.anchor;
3045 if (this.html !== false || this.fa !== false) {
3048 if (this.href !== false) {
3049 cfg.href = this.href;
3053 if(this.alt !== false){
3058 if(this.target !== false) {
3059 cfg.target = this.target;
3065 initEvents: function() {
3067 if(!this.href || this.preventDefault){
3068 this.el.on('click', this.onClick, this);
3072 onClick : function(e)
3074 if(this.preventDefault){
3077 //Roo.log('img onclick');
3078 this.fireEvent('click', this, e);
3091 * @class Roo.bootstrap.Header
3092 * @extends Roo.bootstrap.Component
3093 * Bootstrap Header class
3094 * @cfg {String} html content of header
3095 * @cfg {Number} level (1|2|3|4|5|6) default 1
3098 * Create a new Header
3099 * @param {Object} config The config object
3103 Roo.bootstrap.Header = function(config){
3104 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3115 getAutoCreate : function(){
3120 tag: 'h' + (1 *this.level),
3121 html: this.html || ''
3133 * Ext JS Library 1.1.1
3134 * Copyright(c) 2006-2007, Ext JS, LLC.
3136 * Originally Released Under LGPL - original licence link has changed is not relivant.
3139 * <script type="text/javascript">
3143 * @class Roo.bootstrap.MenuMgr
3144 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3147 Roo.bootstrap.MenuMgr = function(){
3148 var menus, active, groups = {}, attached = false, lastShow = new Date();
3150 // private - called when first menu is created
3153 active = new Roo.util.MixedCollection();
3154 Roo.get(document).addKeyListener(27, function(){
3155 if(active.length > 0){
3163 if(active && active.length > 0){
3164 var c = active.clone();
3174 if(active.length < 1){
3175 Roo.get(document).un("mouseup", onMouseDown);
3183 var last = active.last();
3184 lastShow = new Date();
3187 Roo.get(document).on("mouseup", onMouseDown);
3192 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193 m.parentMenu.activeChild = m;
3194 }else if(last && last.isVisible()){
3195 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3200 function onBeforeHide(m){
3202 m.activeChild.hide();
3204 if(m.autoHideTimer){
3205 clearTimeout(m.autoHideTimer);
3206 delete m.autoHideTimer;
3211 function onBeforeShow(m){
3212 var pm = m.parentMenu;
3213 if(!pm && !m.allowOtherMenus){
3215 }else if(pm && pm.activeChild && active != m){
3216 pm.activeChild.hide();
3220 // private this should really trigger on mouseup..
3221 function onMouseDown(e){
3222 Roo.log("on Mouse Up");
3224 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225 Roo.log("MenuManager hideAll");
3234 function onBeforeCheck(mi, state){
3236 var g = groups[mi.group];
3237 for(var i = 0, l = g.length; i < l; i++){
3239 g[i].setChecked(false);
3248 * Hides all menus that are currently visible
3250 hideAll : function(){
3255 register : function(menu){
3259 menus[menu.id] = menu;
3260 menu.on("beforehide", onBeforeHide);
3261 menu.on("hide", onHide);
3262 menu.on("beforeshow", onBeforeShow);
3263 menu.on("show", onShow);
3265 if(g && menu.events["checkchange"]){
3269 groups[g].push(menu);
3270 menu.on("checkchange", onCheck);
3275 * Returns a {@link Roo.menu.Menu} object
3276 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277 * be used to generate and return a new Menu instance.
3279 get : function(menu){
3280 if(typeof menu == "string"){ // menu id
3282 }else if(menu.events){ // menu instance
3285 /*else if(typeof menu.length == 'number'){ // array of menu items?
3286 return new Roo.bootstrap.Menu({items:menu});
3287 }else{ // otherwise, must be a config
3288 return new Roo.bootstrap.Menu(menu);
3295 unregister : function(menu){
3296 delete menus[menu.id];
3297 menu.un("beforehide", onBeforeHide);
3298 menu.un("hide", onHide);
3299 menu.un("beforeshow", onBeforeShow);
3300 menu.un("show", onShow);
3302 if(g && menu.events["checkchange"]){
3303 groups[g].remove(menu);
3304 menu.un("checkchange", onCheck);
3309 registerCheckable : function(menuItem){
3310 var g = menuItem.group;
3315 groups[g].push(menuItem);
3316 menuItem.on("beforecheckchange", onBeforeCheck);
3321 unregisterCheckable : function(menuItem){
3322 var g = menuItem.group;
3324 groups[g].remove(menuItem);
3325 menuItem.un("beforecheckchange", onBeforeCheck);
3337 * @class Roo.bootstrap.Menu
3338 * @extends Roo.bootstrap.Component
3339 * Bootstrap Menu class - container for MenuItems
3340 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341 * @cfg {bool} hidden if the menu should be hidden when rendered.
3342 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3343 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3347 * @param {Object} config The config object
3351 Roo.bootstrap.Menu = function(config){
3352 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353 if (this.registerMenu && this.type != 'treeview') {
3354 Roo.bootstrap.MenuMgr.register(this);
3361 * Fires before this menu is displayed (return false to block)
3362 * @param {Roo.menu.Menu} this
3367 * Fires before this menu is hidden (return false to block)
3368 * @param {Roo.menu.Menu} this
3373 * Fires after this menu is displayed
3374 * @param {Roo.menu.Menu} this
3379 * Fires after this menu is hidden
3380 * @param {Roo.menu.Menu} this
3385 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386 * @param {Roo.menu.Menu} this
3387 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388 * @param {Roo.EventObject} e
3393 * Fires when the mouse is hovering over this menu
3394 * @param {Roo.menu.Menu} this
3395 * @param {Roo.EventObject} e
3396 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3401 * Fires when the mouse exits this menu
3402 * @param {Roo.menu.Menu} this
3403 * @param {Roo.EventObject} e
3404 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3409 * Fires when a menu item contained in this menu is clicked
3410 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411 * @param {Roo.EventObject} e
3415 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3422 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3425 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3427 registerMenu : true,
3429 menuItems :false, // stores the menu items..
3439 getChildContainer : function() {
3443 getAutoCreate : function(){
3445 //if (['right'].indexOf(this.align)!==-1) {
3446 // cfg.cn[1].cls += ' pull-right'
3452 cls : 'dropdown-menu' ,
3453 style : 'z-index:1000'
3457 if (this.type === 'submenu') {
3458 cfg.cls = 'submenu active';
3460 if (this.type === 'treeview') {
3461 cfg.cls = 'treeview-menu';
3466 initEvents : function() {
3468 // Roo.log("ADD event");
3469 // Roo.log(this.triggerEl.dom);
3471 this.triggerEl.on('click', this.onTriggerClick, this);
3473 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3476 if (this.triggerEl.hasClass('nav-item')) {
3477 // dropdown toggle on the 'a' in BS4?
3478 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3480 this.triggerEl.addClass('dropdown-toggle');
3483 this.el.on('touchstart' , this.onTouch, this);
3485 this.el.on('click' , this.onClick, this);
3487 this.el.on("mouseover", this.onMouseOver, this);
3488 this.el.on("mouseout", this.onMouseOut, this);
3492 findTargetItem : function(e)
3494 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3498 //Roo.log(t); Roo.log(t.id);
3500 //Roo.log(this.menuitems);
3501 return this.menuitems.get(t.id);
3503 //return this.items.get(t.menuItemId);
3509 onTouch : function(e)
3511 Roo.log("menu.onTouch");
3512 //e.stopEvent(); this make the user popdown broken
3516 onClick : function(e)
3518 Roo.log("menu.onClick");
3520 var t = this.findTargetItem(e);
3521 if(!t || t.isContainer){
3526 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3527 if(t == this.activeItem && t.shouldDeactivate(e)){
3528 this.activeItem.deactivate();
3529 delete this.activeItem;
3533 this.setActiveItem(t, true);
3541 Roo.log('pass click event');
3545 this.fireEvent("click", this, t, e);
3549 if(!t.href.length || t.href == '#'){
3550 (function() { _this.hide(); }).defer(100);
3555 onMouseOver : function(e){
3556 var t = this.findTargetItem(e);
3559 // if(t.canActivate && !t.disabled){
3560 // this.setActiveItem(t, true);
3564 this.fireEvent("mouseover", this, e, t);
3566 isVisible : function(){
3567 return !this.hidden;
3569 onMouseOut : function(e){
3570 var t = this.findTargetItem(e);
3573 // if(t == this.activeItem && t.shouldDeactivate(e)){
3574 // this.activeItem.deactivate();
3575 // delete this.activeItem;
3578 this.fireEvent("mouseout", this, e, t);
3583 * Displays this menu relative to another element
3584 * @param {String/HTMLElement/Roo.Element} element The element to align to
3585 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586 * the element (defaults to this.defaultAlign)
3587 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3589 show : function(el, pos, parentMenu)
3591 if (false === this.fireEvent("beforeshow", this)) {
3592 Roo.log("show canceled");
3595 this.parentMenu = parentMenu;
3600 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3603 * Displays this menu at a specific xy position
3604 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3607 showAt : function(xy, parentMenu, /* private: */_e){
3608 this.parentMenu = parentMenu;
3613 this.fireEvent("beforeshow", this);
3614 //xy = this.el.adjustForConstraints(xy);
3618 this.hideMenuItems();
3619 this.hidden = false;
3620 this.triggerEl.addClass('open');
3621 this.el.addClass('show');
3623 // reassign x when hitting right
3624 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3628 // reassign y when hitting bottom
3629 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3633 // but the list may align on trigger left or trigger top... should it be a properity?
3635 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3640 this.fireEvent("show", this);
3646 this.doFocus.defer(50, this);
3650 doFocus : function(){
3652 this.focusEl.focus();
3657 * Hides this menu and optionally all parent menus
3658 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3660 hide : function(deep)
3662 if (false === this.fireEvent("beforehide", this)) {
3663 Roo.log("hide canceled");
3666 this.hideMenuItems();
3667 if(this.el && this.isVisible()){
3669 if(this.activeItem){
3670 this.activeItem.deactivate();
3671 this.activeItem = null;
3673 this.triggerEl.removeClass('open');;
3674 this.el.removeClass('show');
3676 this.fireEvent("hide", this);
3678 if(deep === true && this.parentMenu){
3679 this.parentMenu.hide(true);
3683 onTriggerClick : function(e)
3685 Roo.log('trigger click');
3687 var target = e.getTarget();
3689 Roo.log(target.nodeName.toLowerCase());
3691 if(target.nodeName.toLowerCase() === 'i'){
3697 onTriggerPress : function(e)
3699 Roo.log('trigger press');
3700 //Roo.log(e.getTarget());
3701 // Roo.log(this.triggerEl.dom);
3703 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704 var pel = Roo.get(e.getTarget());
3705 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706 Roo.log('is treeview or dropdown?');
3710 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714 if (this.isVisible()) {
3719 this.show(this.triggerEl, '?', false);
3722 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3729 hideMenuItems : function()
3731 Roo.log("hide Menu Items");
3736 this.el.select('.open',true).each(function(aa) {
3738 aa.removeClass('open');
3742 addxtypeChild : function (tree, cntr) {
3743 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3745 this.menuitems.add(comp);
3757 this.getEl().dom.innerHTML = '';
3758 this.menuitems.clear();
3772 * @class Roo.bootstrap.MenuItem
3773 * @extends Roo.bootstrap.Component
3774 * Bootstrap MenuItem class
3775 * @cfg {String} html the menu label
3776 * @cfg {String} href the link
3777 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779 * @cfg {Boolean} active used on sidebars to highlight active itesm
3780 * @cfg {String} fa favicon to show on left of menu item.
3781 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785 * Create a new MenuItem
3786 * @param {Object} config The config object
3790 Roo.bootstrap.MenuItem = function(config){
3791 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3796 * The raw click event for the entire grid.
3797 * @param {Roo.bootstrap.MenuItem} this
3798 * @param {Roo.EventObject} e
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3808 preventDefault: false,
3809 isContainer : false,
3813 getAutoCreate : function(){
3815 if(this.isContainer){
3818 cls: 'dropdown-menu-item '
3828 cls : 'dropdown-item',
3833 if (this.fa !== false) {
3836 cls : 'fa fa-' + this.fa
3845 cls: 'dropdown-menu-item',
3848 if (this.parent().type == 'treeview') {
3849 cfg.cls = 'treeview-menu';
3852 cfg.cls += ' active';
3857 anc.href = this.href || cfg.cn[0].href ;
3858 ctag.html = this.html || cfg.cn[0].html ;
3862 initEvents: function()
3864 if (this.parent().type == 'treeview') {
3865 this.el.select('a').on('click', this.onClick, this);
3869 this.menu.parentType = this.xtype;
3870 this.menu.triggerEl = this.el;
3871 this.menu = this.addxtype(Roo.apply({}, this.menu));
3875 onClick : function(e)
3877 Roo.log('item on click ');
3879 if(this.preventDefault){
3882 //this.parent().hideMenuItems();
3884 this.fireEvent('click', this, e);
3903 * @class Roo.bootstrap.MenuSeparator
3904 * @extends Roo.bootstrap.Component
3905 * Bootstrap MenuSeparator class
3908 * Create a new MenuItem
3909 * @param {Object} config The config object
3913 Roo.bootstrap.MenuSeparator = function(config){
3914 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3919 getAutoCreate : function(){
3938 * @class Roo.bootstrap.Modal
3939 * @extends Roo.bootstrap.Component
3940 * Bootstrap Modal class
3941 * @cfg {String} title Title of dialog
3942 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3944 * @cfg {Boolean} specificTitle default false
3945 * @cfg {Array} buttons Array of buttons or standard button set..
3946 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947 * @cfg {Boolean} animate default true
3948 * @cfg {Boolean} allow_close default true
3949 * @cfg {Boolean} fitwindow default false
3950 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953 * @cfg {String} size (sm|lg|xl) default empty
3954 * @cfg {Number} max_width set the max width of modal
3955 * @cfg {Boolean} editableTitle can the title be edited
3960 * Create a new Modal Dialog
3961 * @param {Object} config The config object
3964 Roo.bootstrap.Modal = function(config){
3965 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3970 * The raw btnclick event for the button
3971 * @param {Roo.EventObject} e
3976 * Fire when dialog resize
3977 * @param {Roo.bootstrap.Modal} this
3978 * @param {Roo.EventObject} e
3982 * @event titlechanged
3983 * Fire when the editable title has been changed
3984 * @param {Roo.bootstrap.Modal} this
3985 * @param {Roo.EventObject} value
3987 "titlechanged" : true
3990 this.buttons = this.buttons || [];
3993 this.tmpl = Roo.factory(this.tmpl);
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4000 title : 'test dialog',
4010 specificTitle: false,
4012 buttonPosition: 'right',
4034 editableTitle : false,
4036 onRender : function(ct, position)
4038 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4041 var cfg = Roo.apply({}, this.getAutoCreate());
4044 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4046 //if (!cfg.name.length) {
4050 cfg.cls += ' ' + this.cls;
4053 cfg.style = this.style;
4055 this.el = Roo.get(document.body).createChild(cfg, position);
4057 //var type = this.el.dom.type;
4060 if(this.tabIndex !== undefined){
4061 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4064 this.dialogEl = this.el.select('.modal-dialog',true).first();
4065 this.bodyEl = this.el.select('.modal-body',true).first();
4066 this.closeEl = this.el.select('.modal-header .close', true).first();
4067 this.headerEl = this.el.select('.modal-header',true).first();
4068 this.titleEl = this.el.select('.modal-title',true).first();
4069 this.footerEl = this.el.select('.modal-footer',true).first();
4071 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4073 //this.el.addClass("x-dlg-modal");
4075 if (this.buttons.length) {
4076 Roo.each(this.buttons, function(bb) {
4077 var b = Roo.apply({}, bb);
4078 b.xns = b.xns || Roo.bootstrap;
4079 b.xtype = b.xtype || 'Button';
4080 if (typeof(b.listeners) == 'undefined') {
4081 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4084 var btn = Roo.factory(b);
4086 btn.render(this.getButtonContainer());
4090 // render the children.
4093 if(typeof(this.items) != 'undefined'){
4094 var items = this.items;
4097 for(var i =0;i < items.length;i++) {
4098 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4102 this.items = nitems;
4104 // where are these used - they used to be body/close/footer
4108 //this.el.addClass([this.fieldClass, this.cls]);
4112 getAutoCreate : function()
4114 // we will default to modal-body-overflow - might need to remove or make optional later.
4116 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4117 html : this.html || ''
4122 cls : 'modal-title',
4126 if(this.specificTitle){ // WTF is this?
4131 if (this.allow_close && Roo.bootstrap.version == 3) {
4141 if (this.editableTitle) {
4143 cls: 'form-control roo-editable-title d-none',
4149 if (this.allow_close && Roo.bootstrap.version == 4) {
4159 if(this.size.length){
4160 size = 'modal-' + this.size;
4163 var footer = Roo.bootstrap.version == 3 ?
4165 cls : 'modal-footer',
4169 cls: 'btn-' + this.buttonPosition
4174 { // BS4 uses mr-auto on left buttons....
4175 cls : 'modal-footer'
4186 cls: "modal-dialog " + size,
4189 cls : "modal-content",
4192 cls : 'modal-header',
4207 modal.cls += ' fade';
4213 getChildContainer : function() {
4218 getButtonContainer : function() {
4220 return Roo.bootstrap.version == 4 ?
4221 this.el.select('.modal-footer',true).first()
4222 : this.el.select('.modal-footer div',true).first();
4225 initEvents : function()
4227 if (this.allow_close) {
4228 this.closeEl.on('click', this.hide, this);
4230 Roo.EventManager.onWindowResize(this.resize, this, true);
4231 if (this.editableTitle) {
4232 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4233 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234 this.headerEditEl.on('keyup', function(e) {
4235 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236 this.toggleHeaderInput(false)
4239 this.headerEditEl.on('blur', function(e) {
4240 this.toggleHeaderInput(false)
4249 this.maskEl.setSize(
4250 Roo.lib.Dom.getViewWidth(true),
4251 Roo.lib.Dom.getViewHeight(true)
4254 if (this.fitwindow) {
4256 this.dialogEl.setStyle( { 'max-width' : '100%' });
4258 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4264 if(this.max_width !== 0) {
4266 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4269 this.setSize(w, this.height);
4273 if(this.max_height) {
4274 this.setSize(w,Math.min(
4276 Roo.lib.Dom.getViewportHeight(true) - 60
4282 if(!this.fit_content) {
4283 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4287 this.setSize(w, Math.min(
4289 this.headerEl.getHeight() +
4290 this.footerEl.getHeight() +
4291 this.getChildHeight(this.bodyEl.dom.childNodes),
4292 Roo.lib.Dom.getViewportHeight(true) - 60)
4298 setSize : function(w,h)
4309 if (!this.rendered) {
4312 this.toggleHeaderInput(false);
4313 //this.el.setStyle('display', 'block');
4314 this.el.removeClass('hideing');
4315 this.el.dom.style.display='block';
4317 Roo.get(document.body).addClass('modal-open');
4319 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4322 this.el.addClass('show');
4323 this.el.addClass('in');
4326 this.el.addClass('show');
4327 this.el.addClass('in');
4330 // not sure how we can show data in here..
4332 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4335 Roo.get(document.body).addClass("x-body-masked");
4337 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4338 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339 this.maskEl.dom.style.display = 'block';
4340 this.maskEl.addClass('show');
4345 this.fireEvent('show', this);
4347 // set zindex here - otherwise it appears to be ignored...
4348 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4351 this.items.forEach( function(e) {
4352 e.layout ? e.layout() : false;
4360 if(this.fireEvent("beforehide", this) !== false){
4362 this.maskEl.removeClass('show');
4364 this.maskEl.dom.style.display = '';
4365 Roo.get(document.body).removeClass("x-body-masked");
4366 this.el.removeClass('in');
4367 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4369 if(this.animate){ // why
4370 this.el.addClass('hideing');
4371 this.el.removeClass('show');
4373 if (!this.el.hasClass('hideing')) {
4374 return; // it's been shown again...
4377 this.el.dom.style.display='';
4379 Roo.get(document.body).removeClass('modal-open');
4380 this.el.removeClass('hideing');
4384 this.el.removeClass('show');
4385 this.el.dom.style.display='';
4386 Roo.get(document.body).removeClass('modal-open');
4389 this.fireEvent('hide', this);
4392 isVisible : function()
4395 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4399 addButton : function(str, cb)
4403 var b = Roo.apply({}, { html : str } );
4404 b.xns = b.xns || Roo.bootstrap;
4405 b.xtype = b.xtype || 'Button';
4406 if (typeof(b.listeners) == 'undefined') {
4407 b.listeners = { click : cb.createDelegate(this) };
4410 var btn = Roo.factory(b);
4412 btn.render(this.getButtonContainer());
4418 setDefaultButton : function(btn)
4420 //this.el.select('.modal-footer').()
4423 resizeTo: function(w,h)
4425 this.dialogEl.setWidth(w);
4427 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4429 this.bodyEl.setHeight(h - diff);
4431 this.fireEvent('resize', this);
4434 setContentSize : function(w, h)
4438 onButtonClick: function(btn,e)
4441 this.fireEvent('btnclick', btn.name, e);
4444 * Set the title of the Dialog
4445 * @param {String} str new Title
4447 setTitle: function(str) {
4448 this.titleEl.dom.innerHTML = str;
4452 * Set the body of the Dialog
4453 * @param {String} str new Title
4455 setBody: function(str) {
4456 this.bodyEl.dom.innerHTML = str;
4459 * Set the body of the Dialog using the template
4460 * @param {Obj} data - apply this data to the template and replace the body contents.
4462 applyBody: function(obj)
4465 Roo.log("Error - using apply Body without a template");
4468 this.tmpl.overwrite(this.bodyEl, obj);
4471 getChildHeight : function(child_nodes)
4475 child_nodes.length == 0
4480 var child_height = 0;
4482 for(var i = 0; i < child_nodes.length; i++) {
4485 * for modal with tabs...
4486 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4488 var layout_childs = child_nodes[i].childNodes;
4490 for(var j = 0; j < layout_childs.length; j++) {
4492 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4494 var layout_body_childs = layout_childs[j].childNodes;
4496 for(var k = 0; k < layout_body_childs.length; k++) {
4498 if(layout_body_childs[k].classList.contains('navbar')) {
4499 child_height += layout_body_childs[k].offsetHeight;
4503 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4505 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4507 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4509 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4525 child_height += child_nodes[i].offsetHeight;
4526 // Roo.log(child_nodes[i].offsetHeight);
4529 return child_height;
4531 toggleHeaderInput : function(is_edit)
4533 if (!this.editableTitle) {
4534 return; // not editable.
4536 if (is_edit && this.is_header_editing) {
4537 return; // already editing..
4541 this.headerEditEl.dom.value = this.title;
4542 this.headerEditEl.removeClass('d-none');
4543 this.headerEditEl.dom.focus();
4544 this.titleEl.addClass('d-none');
4546 this.is_header_editing = true;
4549 // flip back to not editing.
4550 this.title = this.headerEditEl.dom.value;
4551 this.headerEditEl.addClass('d-none');
4552 this.titleEl.removeClass('d-none');
4553 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554 this.is_header_editing = false;
4555 this.fireEvent('titlechanged', this, this.title);
4564 Roo.apply(Roo.bootstrap.Modal, {
4566 * Button config that displays a single OK button
4575 * Button config that displays Yes and No buttons
4591 * Button config that displays OK and Cancel buttons
4606 * Button config that displays Yes, No and Cancel buttons
4631 * messagebox - can be used as a replace
4635 * @class Roo.MessageBox
4636 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4645 // process text value...
4649 // Show a dialog using config options:
4651 title:'Save Changes?',
4652 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653 buttons: Roo.Msg.YESNOCANCEL,
4660 Roo.bootstrap.MessageBox = function(){
4661 var dlg, opt, mask, waitTimer;
4662 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663 var buttons, activeTextEl, bwidth;
4667 var handleButton = function(button){
4669 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4673 var handleHide = function(){
4675 dlg.el.removeClass(opt.cls);
4678 // Roo.TaskMgr.stop(waitTimer);
4679 // waitTimer = null;
4684 var updateButtons = function(b){
4687 buttons["ok"].hide();
4688 buttons["cancel"].hide();
4689 buttons["yes"].hide();
4690 buttons["no"].hide();
4691 dlg.footerEl.hide();
4695 dlg.footerEl.show();
4696 for(var k in buttons){
4697 if(typeof buttons[k] != "function"){
4700 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701 width += buttons[k].el.getWidth()+15;
4711 var handleEsc = function(d, k, e){
4712 if(opt && opt.closable !== false){
4722 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723 * @return {Roo.BasicDialog} The BasicDialog element
4725 getDialog : function(){
4727 dlg = new Roo.bootstrap.Modal( {
4730 //constraintoviewport:false,
4732 //collapsible : false,
4737 //buttonAlign:"center",
4738 closeClick : function(){
4739 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4742 handleButton("cancel");
4747 dlg.on("hide", handleHide);
4749 //dlg.addKeyListener(27, handleEsc);
4751 this.buttons = buttons;
4752 var bt = this.buttonText;
4753 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4758 bodyEl = dlg.bodyEl.createChild({
4760 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761 '<textarea class="roo-mb-textarea"></textarea>' +
4762 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4764 msgEl = bodyEl.dom.firstChild;
4765 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766 textboxEl.enableDisplayMode();
4767 textboxEl.addKeyListener([10,13], function(){
4768 if(dlg.isVisible() && opt && opt.buttons){
4771 }else if(opt.buttons.yes){
4772 handleButton("yes");
4776 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777 textareaEl.enableDisplayMode();
4778 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779 progressEl.enableDisplayMode();
4781 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782 var pf = progressEl.dom.firstChild;
4784 pp = Roo.get(pf.firstChild);
4785 pp.setHeight(pf.offsetHeight);
4793 * Updates the message box body text
4794 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795 * the XHTML-compliant non-breaking space character '&#160;')
4796 * @return {Roo.MessageBox} This message box
4798 updateText : function(text)
4800 if(!dlg.isVisible() && !opt.width){
4801 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4804 msgEl.innerHTML = text || ' ';
4806 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4809 Math.min(opt.width || cw , this.maxWidth),
4810 Math.max(opt.minWidth || this.minWidth, bwidth)
4813 activeTextEl.setWidth(w);
4815 if(dlg.isVisible()){
4816 dlg.fixedcenter = false;
4818 // to big, make it scroll. = But as usual stupid IE does not support
4821 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4825 bodyEl.dom.style.height = '';
4826 bodyEl.dom.style.overflowY = '';
4829 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4831 bodyEl.dom.style.overflowX = '';
4834 dlg.setContentSize(w, bodyEl.getHeight());
4835 if(dlg.isVisible()){
4836 dlg.fixedcenter = true;
4842 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4843 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846 * @return {Roo.MessageBox} This message box
4848 updateProgress : function(value, text){
4850 this.updateText(text);
4853 if (pp) { // weird bug on my firefox - for some reason this is not defined
4854 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4861 * Returns true if the message box is currently displayed
4862 * @return {Boolean} True if the message box is visible, else false
4864 isVisible : function(){
4865 return dlg && dlg.isVisible();
4869 * Hides the message box if it is displayed
4872 if(this.isVisible()){
4878 * Displays a new message box, or reinitializes an existing message box, based on the config options
4879 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880 * The following config object properties are supported:
4882 Property Type Description
4883 ---------- --------------- ------------------------------------------------------------------------------------
4884 animEl String/Element An id or Element from which the message box should animate as it opens and
4885 closes (defaults to undefined)
4886 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable Boolean False to hide the top-right close button (defaults to true). Note that
4889 progress and wait dialogs will ignore this property and always hide the
4890 close button as they can only be closed programmatically.
4891 cls String A custom CSS class to apply to the message box element
4892 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4893 displayed (defaults to 75)
4894 fn Function A callback function to execute after closing the dialog. The arguments to the
4895 function will be btn (the name of the button that was clicked, if applicable,
4896 e.g. "ok"), and text (the value of the active text field, if applicable).
4897 Progress and wait dialogs will ignore this option since they do not respond to
4898 user actions and can only be closed programmatically, so any required function
4899 should be called by the same code after it closes the dialog.
4900 icon String A CSS class that provides a background image to be used as an icon for
4901 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4903 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4904 modal Boolean False to allow user interaction with the page while the message box is
4905 displayed (defaults to true)
4906 msg String A string that will replace the existing message box body text (defaults
4907 to the XHTML-compliant non-breaking space character ' ')
4908 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4909 progress Boolean True to display a progress bar (defaults to false)
4910 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4913 title String The title text
4914 value String The string value to set into the active textbox element if displayed
4915 wait Boolean True to display a progress bar (defaults to false)
4916 width Number The width of the dialog in pixels
4923 msg: 'Please enter your address:',
4925 buttons: Roo.MessageBox.OKCANCEL,
4928 animEl: 'addAddressBtn'
4931 * @param {Object} config Configuration options
4932 * @return {Roo.MessageBox} This message box
4934 show : function(options)
4937 // this causes nightmares if you show one dialog after another
4938 // especially on callbacks..
4940 if(this.isVisible()){
4943 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4945 Roo.log("New Dialog Message:" + options.msg )
4946 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4950 var d = this.getDialog();
4952 d.setTitle(opt.title || " ");
4953 d.closeEl.setDisplayed(opt.closable !== false);
4954 activeTextEl = textboxEl;
4955 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4960 textareaEl.setHeight(typeof opt.multiline == "number" ?
4961 opt.multiline : this.defaultTextHeight);
4962 activeTextEl = textareaEl;
4971 progressEl.setDisplayed(opt.progress === true);
4973 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4975 this.updateProgress(0);
4976 activeTextEl.dom.value = opt.value || "";
4978 dlg.setDefaultButton(activeTextEl);
4980 var bs = opt.buttons;
4984 }else if(bs && bs.yes){
4985 db = buttons["yes"];
4987 dlg.setDefaultButton(db);
4989 bwidth = updateButtons(opt.buttons);
4990 this.updateText(opt.msg);
4992 d.el.addClass(opt.cls);
4994 d.proxyDrag = opt.proxyDrag === true;
4995 d.modal = opt.modal !== false;
4996 d.mask = opt.modal !== false ? mask : false;
4998 // force it to the end of the z-index stack so it gets a cursor in FF
4999 document.body.appendChild(dlg.el.dom);
5000 d.animateTarget = null;
5001 d.show(options.animEl);
5007 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5008 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009 * and closing the message box when the process is complete.
5010 * @param {String} title The title bar text
5011 * @param {String} msg The message box body text
5012 * @return {Roo.MessageBox} This message box
5014 progress : function(title, msg){
5021 minWidth: this.minProgressWidth,
5028 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029 * If a callback function is passed it will be called after the user clicks the button, and the
5030 * id of the button that was clicked will be passed as the only parameter to the callback
5031 * (could also be the top-right close button).
5032 * @param {String} title The title bar text
5033 * @param {String} msg The message box body text
5034 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035 * @param {Object} scope (optional) The scope of the callback function
5036 * @return {Roo.MessageBox} This message box
5038 alert : function(title, msg, fn, scope)
5053 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5054 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055 * You are responsible for closing the message box when the process is complete.
5056 * @param {String} msg The message box body text
5057 * @param {String} title (optional) The title bar text
5058 * @return {Roo.MessageBox} This message box
5060 wait : function(msg, title){
5071 waitTimer = Roo.TaskMgr.start({
5073 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5081 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084 * @param {String} title The title bar text
5085 * @param {String} msg The message box body text
5086 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087 * @param {Object} scope (optional) The scope of the callback function
5088 * @return {Roo.MessageBox} This message box
5090 confirm : function(title, msg, fn, scope){
5094 buttons: this.YESNO,
5103 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5105 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106 * (could also be the top-right close button) and the text that was entered will be passed as the two
5107 * parameters to the callback.
5108 * @param {String} title The title bar text
5109 * @param {String} msg The message box body text
5110 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111 * @param {Object} scope (optional) The scope of the callback function
5112 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114 * @return {Roo.MessageBox} This message box
5116 prompt : function(title, msg, fn, scope, multiline){
5120 buttons: this.OKCANCEL,
5125 multiline: multiline,
5132 * Button config that displays a single OK button
5137 * Button config that displays Yes and No buttons
5140 YESNO : {yes:true, no:true},
5142 * Button config that displays OK and Cancel buttons
5145 OKCANCEL : {ok:true, cancel:true},
5147 * Button config that displays Yes, No and Cancel buttons
5150 YESNOCANCEL : {yes:true, no:true, cancel:true},
5153 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5156 defaultTextHeight : 75,
5158 * The maximum width in pixels of the message box (defaults to 600)
5163 * The minimum width in pixels of the message box (defaults to 100)
5168 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5169 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5172 minProgressWidth : 250,
5174 * An object containing the default button text strings that can be overriden for localized language support.
5175 * Supported properties are: ok, cancel, yes and no.
5176 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5189 * Shorthand for {@link Roo.MessageBox}
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5201 * @class Roo.bootstrap.Navbar
5202 * @extends Roo.bootstrap.Component
5203 * Bootstrap Navbar class
5206 * Create a new Navbar
5207 * @param {Object} config The config object
5211 Roo.bootstrap.Navbar = function(config){
5212 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5216 * @event beforetoggle
5217 * Fire before toggle the menu
5218 * @param {Roo.EventObject} e
5220 "beforetoggle" : true
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5233 getAutoCreate : function(){
5236 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5240 initEvents :function ()
5242 //Roo.log(this.el.select('.navbar-toggle',true));
5243 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5250 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5252 var size = this.el.getSize();
5253 this.maskEl.setSize(size.width, size.height);
5254 this.maskEl.enableDisplayMode("block");
5263 getChildContainer : function()
5265 if (this.el && this.el.select('.collapse').getCount()) {
5266 return this.el.select('.collapse',true).first();
5281 onToggle : function()
5284 if(this.fireEvent('beforetoggle', this) === false){
5287 var ce = this.el.select('.navbar-collapse',true).first();
5289 if (!ce.hasClass('show')) {
5299 * Expand the navbar pulldown
5301 expand : function ()
5304 var ce = this.el.select('.navbar-collapse',true).first();
5305 if (ce.hasClass('collapsing')) {
5308 ce.dom.style.height = '';
5310 ce.addClass('in'); // old...
5311 ce.removeClass('collapse');
5312 ce.addClass('show');
5313 var h = ce.getHeight();
5315 ce.removeClass('show');
5316 // at this point we should be able to see it..
5317 ce.addClass('collapsing');
5319 ce.setHeight(0); // resize it ...
5320 ce.on('transitionend', function() {
5321 //Roo.log('done transition');
5322 ce.removeClass('collapsing');
5323 ce.addClass('show');
5324 ce.removeClass('collapse');
5326 ce.dom.style.height = '';
5327 }, this, { single: true} );
5329 ce.dom.scrollTop = 0;
5332 * Collapse the navbar pulldown
5334 collapse : function()
5336 var ce = this.el.select('.navbar-collapse',true).first();
5338 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339 // it's collapsed or collapsing..
5342 ce.removeClass('in'); // old...
5343 ce.setHeight(ce.getHeight());
5344 ce.removeClass('show');
5345 ce.addClass('collapsing');
5347 ce.on('transitionend', function() {
5348 ce.dom.style.height = '';
5349 ce.removeClass('collapsing');
5350 ce.addClass('collapse');
5351 }, this, { single: true} );
5371 * @class Roo.bootstrap.NavSimplebar
5372 * @extends Roo.bootstrap.Navbar
5373 * Bootstrap Sidebar class
5375 * @cfg {Boolean} inverse is inverted color
5377 * @cfg {String} type (nav | pills | tabs)
5378 * @cfg {Boolean} arrangement stacked | justified
5379 * @cfg {String} align (left | right) alignment
5381 * @cfg {Boolean} main (true|false) main nav bar? default false
5382 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5384 * @cfg {String} tag (header|footer|nav|div) default is nav
5386 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5390 * Create a new Sidebar
5391 * @param {Object} config The config object
5395 Roo.bootstrap.NavSimplebar = function(config){
5396 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5415 getAutoCreate : function(){
5419 tag : this.tag || 'div',
5420 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5422 if (['light','white'].indexOf(this.weight) > -1) {
5423 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5425 cfg.cls += ' bg-' + this.weight;
5428 cfg.cls += ' navbar-inverse';
5432 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5434 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5443 cls: 'nav nav-' + this.xtype,
5449 this.type = this.type || 'nav';
5450 if (['tabs','pills'].indexOf(this.type) != -1) {
5451 cfg.cn[0].cls += ' nav-' + this.type
5455 if (this.type!=='nav') {
5456 Roo.log('nav type must be nav/tabs/pills')
5458 cfg.cn[0].cls += ' navbar-nav'
5464 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465 cfg.cn[0].cls += ' nav-' + this.arrangement;
5469 if (this.align === 'right') {
5470 cfg.cn[0].cls += ' navbar-right';
5495 * navbar-expand-md fixed-top
5499 * @class Roo.bootstrap.NavHeaderbar
5500 * @extends Roo.bootstrap.NavSimplebar
5501 * Bootstrap Sidebar class
5503 * @cfg {String} brand what is brand
5504 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505 * @cfg {String} brand_href href of the brand
5506 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5507 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5512 * Create a new Sidebar
5513 * @param {Object} config The config object
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5529 desktopCenter : false,
5532 getAutoCreate : function(){
5535 tag: this.nav || 'nav',
5536 cls: 'navbar navbar-expand-md',
5542 if (this.desktopCenter) {
5543 cn.push({cls : 'container', cn : []});
5551 cls: 'navbar-toggle navbar-toggler',
5552 'data-toggle': 'collapse',
5557 html: 'Toggle navigation'
5561 cls: 'icon-bar navbar-toggler-icon'
5574 cn.push( Roo.bootstrap.version == 4 ? btn : {
5576 cls: 'navbar-header',
5585 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5589 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5591 if (['light','white'].indexOf(this.weight) > -1) {
5592 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5594 cfg.cls += ' bg-' + this.weight;
5597 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5600 // tag can override this..
5602 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5605 if (this.brand !== '') {
5606 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5609 href: this.brand_href ? this.brand_href : '#',
5610 cls: 'navbar-brand',
5618 cfg.cls += ' main-nav';
5626 getHeaderChildContainer : function()
5628 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629 return this.el.select('.navbar-header',true).first();
5632 return this.getChildContainer();
5635 getChildContainer : function()
5638 return this.el.select('.roo-navbar-collapse',true).first();
5643 initEvents : function()
5645 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5647 if (this.autohide) {
5652 Roo.get(document).on('scroll',function(e) {
5653 var ns = Roo.get(document).getScroll().top;
5654 var os = prevScroll;
5658 ft.removeClass('slideDown');
5659 ft.addClass('slideUp');
5662 ft.removeClass('slideUp');
5663 ft.addClass('slideDown');
5684 * @class Roo.bootstrap.NavSidebar
5685 * @extends Roo.bootstrap.Navbar
5686 * Bootstrap Sidebar class
5689 * Create a new Sidebar
5690 * @param {Object} config The config object
5694 Roo.bootstrap.NavSidebar = function(config){
5695 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5700 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5702 getAutoCreate : function(){
5707 cls: 'sidebar sidebar-nav'
5729 * @class Roo.bootstrap.NavGroup
5730 * @extends Roo.bootstrap.Component
5731 * Bootstrap NavGroup class
5732 * @cfg {String} align (left|right)
5733 * @cfg {Boolean} inverse
5734 * @cfg {String} type (nav|pills|tab) default nav
5735 * @cfg {String} navId - reference Id for navbar.
5736 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5739 * Create a new nav group
5740 * @param {Object} config The config object
5743 Roo.bootstrap.NavGroup = function(config){
5744 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5747 Roo.bootstrap.NavGroup.register(this);
5751 * Fires when the active item changes
5752 * @param {Roo.bootstrap.NavGroup} this
5753 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5773 getAutoCreate : function()
5775 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5781 if (Roo.bootstrap.version == 4) {
5782 if (['tabs','pills'].indexOf(this.type) != -1) {
5783 cfg.cls += ' nav-' + this.type;
5785 // trying to remove so header bar can right align top?
5786 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787 // do not use on header bar...
5788 cfg.cls += ' navbar-nav';
5793 if (['tabs','pills'].indexOf(this.type) != -1) {
5794 cfg.cls += ' nav-' + this.type
5796 if (this.type !== 'nav') {
5797 Roo.log('nav type must be nav/tabs/pills')
5799 cfg.cls += ' navbar-nav'
5803 if (this.parent() && this.parent().sidebar) {
5806 cls: 'dashboard-menu sidebar-menu'
5812 if (this.form === true) {
5815 cls: 'navbar-form form-inline'
5817 //nav navbar-right ml-md-auto
5818 if (this.align === 'right') {
5819 cfg.cls += ' navbar-right ml-md-auto';
5821 cfg.cls += ' navbar-left';
5825 if (this.align === 'right') {
5826 cfg.cls += ' navbar-right ml-md-auto';
5828 cfg.cls += ' mr-auto';
5832 cfg.cls += ' navbar-inverse';
5840 * sets the active Navigation item
5841 * @param {Roo.bootstrap.NavItem} the new current navitem
5843 setActiveItem : function(item)
5846 Roo.each(this.navItems, function(v){
5851 v.setActive(false, true);
5858 item.setActive(true, true);
5859 this.fireEvent('changed', this, item, prev);
5864 * gets the active Navigation item
5865 * @return {Roo.bootstrap.NavItem} the current navitem
5867 getActive : function()
5871 Roo.each(this.navItems, function(v){
5882 indexOfNav : function()
5886 Roo.each(this.navItems, function(v,i){
5897 * adds a Navigation item
5898 * @param {Roo.bootstrap.NavItem} the navitem to add
5900 addItem : function(cfg)
5902 if (this.form && Roo.bootstrap.version == 4) {
5905 var cn = new Roo.bootstrap.NavItem(cfg);
5907 cn.parentId = this.id;
5908 cn.onRender(this.el, null);
5912 * register a Navigation item
5913 * @param {Roo.bootstrap.NavItem} the navitem to add
5915 register : function(item)
5917 this.navItems.push( item);
5918 item.navId = this.navId;
5923 * clear all the Navigation item
5926 clearAll : function()
5929 this.el.dom.innerHTML = '';
5932 getNavItem: function(tabId)
5935 Roo.each(this.navItems, function(e) {
5936 if (e.tabId == tabId) {
5946 setActiveNext : function()
5948 var i = this.indexOfNav(this.getActive());
5949 if (i > this.navItems.length) {
5952 this.setActiveItem(this.navItems[i+1]);
5954 setActivePrev : function()
5956 var i = this.indexOfNav(this.getActive());
5960 this.setActiveItem(this.navItems[i-1]);
5962 clearWasActive : function(except) {
5963 Roo.each(this.navItems, function(e) {
5964 if (e.tabId != except.tabId && e.was_active) {
5965 e.was_active = false;
5972 getWasActive : function ()
5975 Roo.each(this.navItems, function(e) {
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5994 * register a Navigation Group
5995 * @param {Roo.bootstrap.NavGroup} the navgroup to add
5997 register : function(navgrp)
5999 this.groups[navgrp.navId] = navgrp;
6003 * fetch a Navigation Group based on the navigation ID
6004 * @param {string} the navgroup to add
6005 * @returns {Roo.bootstrap.NavGroup} the navgroup
6007 get: function(navId) {
6008 if (typeof(this.groups[navId]) == 'undefined') {
6010 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6012 return this.groups[navId] ;
6027 * @class Roo.bootstrap.NavItem
6028 * @extends Roo.bootstrap.Component
6029 * Bootstrap Navbar.NavItem class
6030 * @cfg {String} href link to
6031 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032 * @cfg {Boolean} button_outline show and outlined button
6033 * @cfg {String} html content of button
6034 * @cfg {String} badge text inside badge
6035 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036 * @cfg {String} glyphicon DEPRICATED - use fa
6037 * @cfg {String} icon DEPRICATED - use fa
6038 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039 * @cfg {Boolean} active Is item active
6040 * @cfg {Boolean} disabled Is item disabled
6041 * @cfg {String} linkcls Link Class
6042 * @cfg {Boolean} preventDefault (true | false) default false
6043 * @cfg {String} tabId the tab that this item activates.
6044 * @cfg {String} tagtype (a|span) render as a href or span?
6045 * @cfg {Boolean} animateRef (true|false) link to element default false
6048 * Create a new Navbar Item
6049 * @param {Object} config The config object
6051 Roo.bootstrap.NavItem = function(config){
6052 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6057 * The raw click event for the entire grid.
6058 * @param {Roo.EventObject} e
6063 * Fires when the active item active state changes
6064 * @param {Roo.bootstrap.NavItem} this
6065 * @param {boolean} state the new state
6071 * Fires when scroll to element
6072 * @param {Roo.bootstrap.NavItem} this
6073 * @param {Object} options
6074 * @param {Roo.EventObject} e
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6091 preventDefault : false,
6099 button_outline : false,
6103 getAutoCreate : function(){
6110 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6113 cfg.cls += ' active' ;
6115 if (this.disabled) {
6116 cfg.cls += ' disabled';
6120 if (this.button_weight.length) {
6121 cfg.tag = this.href ? 'a' : 'button';
6122 cfg.html = this.html || '';
6123 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6125 cfg.href = this.href;
6128 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6131 // menu .. should add dropdown-menu class - so no need for carat..
6133 if (this.badge !== '') {
6135 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6140 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6144 href : this.href || "#",
6145 html: this.html || ''
6148 if (this.tagtype == 'a') {
6149 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6153 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6156 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6158 if(this.glyphicon) {
6159 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6164 cfg.cn[0].html += " <span class='caret'></span>";
6168 if (this.badge !== '') {
6170 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6178 onRender : function(ct, position)
6180 // Roo.log("Call onRender: " + this.xtype);
6181 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6185 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186 this.navLink = this.el.select('.nav-link',true).first();
6191 initEvents: function()
6193 if (typeof (this.menu) != 'undefined') {
6194 this.menu.parentType = this.xtype;
6195 this.menu.triggerEl = this.el;
6196 this.menu = this.addxtype(Roo.apply({}, this.menu));
6199 this.el.on('click', this.onClick, this);
6201 //if(this.tagtype == 'span'){
6202 // this.el.select('span',true).on('click', this.onClick, this);
6205 // at this point parent should be available..
6206 this.parent().register(this);
6209 onClick : function(e)
6211 if (e.getTarget('.dropdown-menu-item')) {
6212 // did you click on a menu itemm.... - then don't trigger onclick..
6217 this.preventDefault ||
6220 Roo.log("NavItem - prevent Default?");
6224 if (this.disabled) {
6228 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229 if (tg && tg.transition) {
6230 Roo.log("waiting for the transitionend");
6236 //Roo.log("fire event clicked");
6237 if(this.fireEvent('click', this, e) === false){
6241 if(this.tagtype == 'span'){
6245 //Roo.log(this.href);
6246 var ael = this.el.select('a',true).first();
6249 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252 return; // ignore... - it's a 'hash' to another page.
6254 Roo.log("NavItem - prevent Default?");
6256 this.scrollToElement(e);
6260 var p = this.parent();
6262 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263 if (typeof(p.setActiveItem) !== 'undefined') {
6264 p.setActiveItem(this);
6268 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270 // remove the collapsed menu expand...
6271 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6275 isActive: function () {
6278 setActive : function(state, fire, is_was_active)
6280 if (this.active && !state && this.navId) {
6281 this.was_active = true;
6282 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6284 nv.clearWasActive(this);
6288 this.active = state;
6291 this.el.removeClass('active');
6292 this.navLink ? this.navLink.removeClass('active') : false;
6293 } else if (!this.el.hasClass('active')) {
6295 this.el.addClass('active');
6296 if (Roo.bootstrap.version == 4 && this.navLink ) {
6297 this.navLink.addClass('active');
6302 this.fireEvent('changed', this, state);
6305 // show a panel if it's registered and related..
6307 if (!this.navId || !this.tabId || !state || is_was_active) {
6311 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6315 var pan = tg.getPanelByName(this.tabId);
6319 // if we can not flip to new panel - go back to old nav highlight..
6320 if (false == tg.showPanel(pan)) {
6321 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6323 var onav = nv.getWasActive();
6325 onav.setActive(true, false, true);
6334 // this should not be here...
6335 setDisabled : function(state)
6337 this.disabled = state;
6339 this.el.removeClass('disabled');
6340 } else if (!this.el.hasClass('disabled')) {
6341 this.el.addClass('disabled');
6347 * Fetch the element to display the tooltip on.
6348 * @return {Roo.Element} defaults to this.el
6350 tooltipEl : function()
6352 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6355 scrollToElement : function(e)
6357 var c = document.body;
6360 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6362 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363 c = document.documentElement;
6366 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6372 var o = target.calcOffsetsTo(c);
6379 this.fireEvent('scrollto', this, options, e);
6381 Roo.get(c).scrollTo('top', options.value, true);
6394 * <span> icon </span>
6395 * <span> text </span>
6396 * <span>badge </span>
6400 * @class Roo.bootstrap.NavSidebarItem
6401 * @extends Roo.bootstrap.NavItem
6402 * Bootstrap Navbar.NavSidebarItem class
6403 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404 * {Boolean} open is the menu open
6405 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407 * {String} buttonSize (sm|md|lg)the extra classes for the button
6408 * {Boolean} showArrow show arrow next to the text (default true)
6410 * Create a new Navbar Button
6411 * @param {Object} config The config object
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6419 * The raw click event for the entire grid.
6420 * @param {Roo.EventObject} e
6425 * Fires when the active item active state changes
6426 * @param {Roo.bootstrap.NavSidebarItem} this
6427 * @param {boolean} state the new state
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6437 badgeWeight : 'default',
6443 buttonWeight : 'default',
6449 getAutoCreate : function(){
6454 href : this.href || '#',
6460 if(this.buttonView){
6463 href : this.href || '#',
6464 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6477 cfg.cls += ' active';
6480 if (this.disabled) {
6481 cfg.cls += ' disabled';
6484 cfg.cls += ' open x-open';
6487 if (this.glyphicon || this.icon) {
6488 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6489 a.cn.push({ tag : 'i', cls : c }) ;
6492 if(!this.buttonView){
6495 html : this.html || ''
6502 if (this.badge !== '') {
6503 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6509 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6512 a.cls += ' dropdown-toggle treeview' ;
6518 initEvents : function()
6520 if (typeof (this.menu) != 'undefined') {
6521 this.menu.parentType = this.xtype;
6522 this.menu.triggerEl = this.el;
6523 this.menu = this.addxtype(Roo.apply({}, this.menu));
6526 this.el.on('click', this.onClick, this);
6528 if(this.badge !== ''){
6529 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6534 onClick : function(e)
6541 if(this.preventDefault){
6545 this.fireEvent('click', this, e);
6548 disable : function()
6550 this.setDisabled(true);
6555 this.setDisabled(false);
6558 setDisabled : function(state)
6560 if(this.disabled == state){
6564 this.disabled = state;
6567 this.el.addClass('disabled');
6571 this.el.removeClass('disabled');
6576 setActive : function(state)
6578 if(this.active == state){
6582 this.active = state;
6585 this.el.addClass('active');
6589 this.el.removeClass('active');
6594 isActive: function ()
6599 setBadge : function(str)
6605 this.badgeEl.dom.innerHTML = str;
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6624 * @class Roo.bootstrap.breadcrumb.Nav
6625 * @extends Roo.bootstrap.Component
6626 * Bootstrap Breadcrumb Nav Class
6628 * @children Roo.bootstrap.breadcrumb.Item
6631 * Create a new breadcrumb.Nav
6632 * @param {Object} config The config object
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6644 getAutoCreate : function()
6661 initEvents: function()
6663 this.olEl = this.el.select('ol',true).first();
6665 getChildContainer : function()
6681 * @class Roo.bootstrap.breadcrumb.Nav
6682 * @extends Roo.bootstrap.Component
6683 * Bootstrap Breadcrumb Nav Class
6685 * @children Roo.bootstrap.breadcrumb.Component
6686 * @cfg {String} html the content of the link.
6687 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688 * @cfg {Boolean} active is it active
6692 * Create a new breadcrumb.Nav
6693 * @param {Object} config The config object
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6702 * The img click event for the img.
6703 * @param {Roo.EventObject} e
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6715 getAutoCreate : function()
6720 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6722 if (this.href !== false) {
6729 cfg.html = this.html;
6735 initEvents: function()
6738 this.el.select('a', true).first().on('click',this.onClick, this)
6742 onClick : function(e)
6745 this.fireEvent('click',this, e);
6758 * @class Roo.bootstrap.Row
6759 * @extends Roo.bootstrap.Component
6760 * Bootstrap Row class (contains columns...)
6764 * @param {Object} config The config object
6767 Roo.bootstrap.Row = function(config){
6768 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6773 getAutoCreate : function(){
6792 * @class Roo.bootstrap.Pagination
6793 * @extends Roo.bootstrap.Component
6794 * Bootstrap Pagination class
6795 * @cfg {String} size xs | sm | md | lg
6796 * @cfg {Boolean} inverse false | true
6799 * Create a new Pagination
6800 * @param {Object} config The config object
6803 Roo.bootstrap.Pagination = function(config){
6804 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6813 getAutoCreate : function(){
6819 cfg.cls += ' inverse';
6825 cfg.cls += " " + this.cls;
6843 * @class Roo.bootstrap.PaginationItem
6844 * @extends Roo.bootstrap.Component
6845 * Bootstrap PaginationItem class
6846 * @cfg {String} html text
6847 * @cfg {String} href the link
6848 * @cfg {Boolean} preventDefault (true | false) default true
6849 * @cfg {Boolean} active (true | false) default false
6850 * @cfg {Boolean} disabled default false
6854 * Create a new PaginationItem
6855 * @param {Object} config The config object
6859 Roo.bootstrap.PaginationItem = function(config){
6860 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6865 * The raw click event for the entire grid.
6866 * @param {Roo.EventObject} e
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6876 preventDefault: true,
6881 getAutoCreate : function(){
6887 href : this.href ? this.href : '#',
6888 html : this.html ? this.html : ''
6898 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6902 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6908 initEvents: function() {
6910 this.el.on('click', this.onClick, this);
6913 onClick : function(e)
6915 Roo.log('PaginationItem on click ');
6916 if(this.preventDefault){
6924 this.fireEvent('click', this, e);
6940 * @class Roo.bootstrap.Slider
6941 * @extends Roo.bootstrap.Component
6942 * Bootstrap Slider class
6945 * Create a new Slider
6946 * @param {Object} config The config object
6949 Roo.bootstrap.Slider = function(config){
6950 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6955 getAutoCreate : function(){
6959 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6963 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6975 * Ext JS Library 1.1.1
6976 * Copyright(c) 2006-2007, Ext JS, LLC.
6978 * Originally Released Under LGPL - original licence link has changed is not relivant.
6981 * <script type="text/javascript">
6986 * @class Roo.grid.ColumnModel
6987 * @extends Roo.util.Observable
6988 * This is the default implementation of a ColumnModel used by the Grid. It defines
6989 * the columns in the grid.
6992 var colModel = new Roo.grid.ColumnModel([
6993 {header: "Ticker", width: 60, sortable: true, locked: true},
6994 {header: "Company Name", width: 150, sortable: true},
6995 {header: "Market Cap.", width: 100, sortable: true},
6996 {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997 {header: "Employees", width: 100, sortable: true, resizable: false}
7002 * The config options listed for this class are options which may appear in each
7003 * individual column definition.
7004 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7006 * @param {Object} config An Array of column config objects. See this class's
7007 * config objects for details.
7009 Roo.grid.ColumnModel = function(config){
7011 * The config passed into the constructor
7013 this.config = config;
7016 // if no id, create one
7017 // if the column does not have a dataIndex mapping,
7018 // map it to the order it is in the config
7019 for(var i = 0, len = config.length; i < len; i++){
7021 if(typeof c.dataIndex == "undefined"){
7024 if(typeof c.renderer == "string"){
7025 c.renderer = Roo.util.Format[c.renderer];
7027 if(typeof c.id == "undefined"){
7030 if(c.editor && c.editor.xtype){
7031 c.editor = Roo.factory(c.editor, Roo.grid);
7033 if(c.editor && c.editor.isFormField){
7034 c.editor = new Roo.grid.GridEditor(c.editor);
7036 this.lookup[c.id] = c;
7040 * The width of columns which have no width specified (defaults to 100)
7043 this.defaultWidth = 100;
7046 * Default sortable of columns which have no sortable specified (defaults to false)
7049 this.defaultSortable = false;
7053 * @event widthchange
7054 * Fires when the width of a column changes.
7055 * @param {ColumnModel} this
7056 * @param {Number} columnIndex The column index
7057 * @param {Number} newWidth The new width
7059 "widthchange": true,
7061 * @event headerchange
7062 * Fires when the text of a header changes.
7063 * @param {ColumnModel} this
7064 * @param {Number} columnIndex The column index
7065 * @param {Number} newText The new header text
7067 "headerchange": true,
7069 * @event hiddenchange
7070 * Fires when a column is hidden or "unhidden".
7071 * @param {ColumnModel} this
7072 * @param {Number} columnIndex The column index
7073 * @param {Boolean} hidden true if hidden, false otherwise
7075 "hiddenchange": true,
7077 * @event columnmoved
7078 * Fires when a column is moved.
7079 * @param {ColumnModel} this
7080 * @param {Number} oldIndex
7081 * @param {Number} newIndex
7083 "columnmoved" : true,
7085 * @event columlockchange
7086 * Fires when a column's locked state is changed
7087 * @param {ColumnModel} this
7088 * @param {Number} colIndex
7089 * @param {Boolean} locked true if locked
7091 "columnlockchange" : true
7093 Roo.grid.ColumnModel.superclass.constructor.call(this);
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7097 * @cfg {String} header The header text to display in the Grid view.
7100 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102 * specified, the column's index is used as an index into the Record's data Array.
7105 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7109 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110 * Defaults to the value of the {@link #defaultSortable} property.
7111 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7114 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7117 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7120 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7123 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7126 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7132 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7135 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7138 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7141 * @cfg {String} cursor (Optional)
7144 * @cfg {String} tooltip (Optional)
7147 * @cfg {Number} xs (Optional)
7150 * @cfg {Number} sm (Optional)
7153 * @cfg {Number} md (Optional)
7156 * @cfg {Number} lg (Optional)
7159 * Returns the id of the column at the specified index.
7160 * @param {Number} index The column index
7161 * @return {String} the id
7163 getColumnId : function(index){
7164 return this.config[index].id;
7168 * Returns the column for a specified id.
7169 * @param {String} id The column id
7170 * @return {Object} the column
7172 getColumnById : function(id){
7173 return this.lookup[id];
7178 * Returns the column for a specified dataIndex.
7179 * @param {String} dataIndex The column dataIndex
7180 * @return {Object|Boolean} the column or false if not found
7182 getColumnByDataIndex: function(dataIndex){
7183 var index = this.findColumnIndex(dataIndex);
7184 return index > -1 ? this.config[index] : false;
7188 * Returns the index for a specified column id.
7189 * @param {String} id The column id
7190 * @return {Number} the index, or -1 if not found
7192 getIndexById : function(id){
7193 for(var i = 0, len = this.config.length; i < len; i++){
7194 if(this.config[i].id == id){
7202 * Returns the index for a specified column dataIndex.
7203 * @param {String} dataIndex The column dataIndex
7204 * @return {Number} the index, or -1 if not found
7207 findColumnIndex : function(dataIndex){
7208 for(var i = 0, len = this.config.length; i < len; i++){
7209 if(this.config[i].dataIndex == dataIndex){
7217 moveColumn : function(oldIndex, newIndex){
7218 var c = this.config[oldIndex];
7219 this.config.splice(oldIndex, 1);
7220 this.config.splice(newIndex, 0, c);
7221 this.dataMap = null;
7222 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7225 isLocked : function(colIndex){
7226 return this.config[colIndex].locked === true;
7229 setLocked : function(colIndex, value, suppressEvent){
7230 if(this.isLocked(colIndex) == value){
7233 this.config[colIndex].locked = value;
7235 this.fireEvent("columnlockchange", this, colIndex, value);
7239 getTotalLockedWidth : function(){
7241 for(var i = 0; i < this.config.length; i++){
7242 if(this.isLocked(i) && !this.isHidden(i)){
7243 this.totalWidth += this.getColumnWidth(i);
7249 getLockedCount : function(){
7250 for(var i = 0, len = this.config.length; i < len; i++){
7251 if(!this.isLocked(i)){
7256 return this.config.length;
7260 * Returns the number of columns.
7263 getColumnCount : function(visibleOnly){
7264 if(visibleOnly === true){
7266 for(var i = 0, len = this.config.length; i < len; i++){
7267 if(!this.isHidden(i)){
7273 return this.config.length;
7277 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278 * @param {Function} fn
7279 * @param {Object} scope (optional)
7280 * @return {Array} result
7282 getColumnsBy : function(fn, scope){
7284 for(var i = 0, len = this.config.length; i < len; i++){
7285 var c = this.config[i];
7286 if(fn.call(scope||this, c, i) === true){
7294 * Returns true if the specified column is sortable.
7295 * @param {Number} col The column index
7298 isSortable : function(col){
7299 if(typeof this.config[col].sortable == "undefined"){
7300 return this.defaultSortable;
7302 return this.config[col].sortable;
7306 * Returns the rendering (formatting) function defined for the column.
7307 * @param {Number} col The column index.
7308 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7310 getRenderer : function(col){
7311 if(!this.config[col].renderer){
7312 return Roo.grid.ColumnModel.defaultRenderer;
7314 return this.config[col].renderer;
7318 * Sets the rendering (formatting) function for a column.
7319 * @param {Number} col The column index
7320 * @param {Function} fn The function to use to process the cell's raw data
7321 * to return HTML markup for the grid view. The render function is called with
7322 * the following parameters:<ul>
7323 * <li>Data value.</li>
7324 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325 * <li>css A CSS style string to apply to the table cell.</li>
7326 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328 * <li>Row index</li>
7329 * <li>Column index</li>
7330 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7332 setRenderer : function(col, fn){
7333 this.config[col].renderer = fn;
7337 * Returns the width for the specified column.
7338 * @param {Number} col The column index
7341 getColumnWidth : function(col){
7342 return this.config[col].width * 1 || this.defaultWidth;
7346 * Sets the width for a column.
7347 * @param {Number} col The column index
7348 * @param {Number} width The new width
7350 setColumnWidth : function(col, width, suppressEvent){
7351 this.config[col].width = width;
7352 this.totalWidth = null;
7354 this.fireEvent("widthchange", this, col, width);
7359 * Returns the total width of all columns.
7360 * @param {Boolean} includeHidden True to include hidden column widths
7363 getTotalWidth : function(includeHidden){
7364 if(!this.totalWidth){
7365 this.totalWidth = 0;
7366 for(var i = 0, len = this.config.length; i < len; i++){
7367 if(includeHidden || !this.isHidden(i)){
7368 this.totalWidth += this.getColumnWidth(i);
7372 return this.totalWidth;
7376 * Returns the header for the specified column.
7377 * @param {Number} col The column index
7380 getColumnHeader : function(col){
7381 return this.config[col].header;
7385 * Sets the header for a column.
7386 * @param {Number} col The column index
7387 * @param {String} header The new header
7389 setColumnHeader : function(col, header){
7390 this.config[col].header = header;
7391 this.fireEvent("headerchange", this, col, header);
7395 * Returns the tooltip for the specified column.
7396 * @param {Number} col The column index
7399 getColumnTooltip : function(col){
7400 return this.config[col].tooltip;
7403 * Sets the tooltip for a column.
7404 * @param {Number} col The column index
7405 * @param {String} tooltip The new tooltip
7407 setColumnTooltip : function(col, tooltip){
7408 this.config[col].tooltip = tooltip;
7412 * Returns the dataIndex for the specified column.
7413 * @param {Number} col The column index
7416 getDataIndex : function(col){
7417 return this.config[col].dataIndex;
7421 * Sets the dataIndex for a column.
7422 * @param {Number} col The column index
7423 * @param {Number} dataIndex The new dataIndex
7425 setDataIndex : function(col, dataIndex){
7426 this.config[col].dataIndex = dataIndex;
7432 * Returns true if the cell is editable.
7433 * @param {Number} colIndex The column index
7434 * @param {Number} rowIndex The row index - this is nto actually used..?
7437 isCellEditable : function(colIndex, rowIndex){
7438 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7442 * Returns the editor defined for the cell/column.
7443 * return false or null to disable editing.
7444 * @param {Number} colIndex The column index
7445 * @param {Number} rowIndex The row index
7448 getCellEditor : function(colIndex, rowIndex){
7449 return this.config[colIndex].editor;
7453 * Sets if a column is editable.
7454 * @param {Number} col The column index
7455 * @param {Boolean} editable True if the column is editable
7457 setEditable : function(col, editable){
7458 this.config[col].editable = editable;
7463 * Returns true if the column is hidden.
7464 * @param {Number} colIndex The column index
7467 isHidden : function(colIndex){
7468 return this.config[colIndex].hidden;
7473 * Returns true if the column width cannot be changed
7475 isFixed : function(colIndex){
7476 return this.config[colIndex].fixed;
7480 * Returns true if the column can be resized
7483 isResizable : function(colIndex){
7484 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7487 * Sets if a column is hidden.
7488 * @param {Number} colIndex The column index
7489 * @param {Boolean} hidden True if the column is hidden
7491 setHidden : function(colIndex, hidden){
7492 this.config[colIndex].hidden = hidden;
7493 this.totalWidth = null;
7494 this.fireEvent("hiddenchange", this, colIndex, hidden);
7498 * Sets the editor for a column.
7499 * @param {Number} col The column index
7500 * @param {Object} editor The editor object
7502 setEditor : function(col, editor){
7503 this.config[col].editor = editor;
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7509 if(typeof value == "object") {
7512 if(typeof value == "string" && value.length < 1){
7516 return String.format("{0}", value);
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7523 * Ext JS Library 1.1.1
7524 * Copyright(c) 2006-2007, Ext JS, LLC.
7526 * Originally Released Under LGPL - original licence link has changed is not relivant.
7529 * <script type="text/javascript">
7533 * @class Roo.LoadMask
7534 * A simple utility class for generically masking elements while loading data. If the element being masked has
7535 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7537 * element's UpdateManager load indicator and will be destroyed after the initial load.
7539 * Create a new LoadMask
7540 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541 * @param {Object} config The config object
7543 Roo.LoadMask = function(el, config){
7544 this.el = Roo.get(el);
7545 Roo.apply(this, config);
7547 this.store.on('beforeload', this.onBeforeLoad, this);
7548 this.store.on('load', this.onLoad, this);
7549 this.store.on('loadexception', this.onLoadException, this);
7550 this.removeMask = false;
7552 var um = this.el.getUpdateManager();
7553 um.showLoadIndicator = false; // disable the default indicator
7554 um.on('beforeupdate', this.onBeforeLoad, this);
7555 um.on('update', this.onLoad, this);
7556 um.on('failure', this.onLoad, this);
7557 this.removeMask = true;
7561 Roo.LoadMask.prototype = {
7563 * @cfg {Boolean} removeMask
7564 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7569 * The text to display in a centered loading message box (defaults to 'Loading...')
7573 * @cfg {String} msgCls
7574 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7576 msgCls : 'x-mask-loading',
7579 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7585 * Disables the mask to prevent it from being displayed
7587 disable : function(){
7588 this.disabled = true;
7592 * Enables the mask so that it can be displayed
7594 enable : function(){
7595 this.disabled = false;
7598 onLoadException : function()
7602 if (typeof(arguments[3]) != 'undefined') {
7603 Roo.MessageBox.alert("Error loading",arguments[3]);
7607 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7615 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7620 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7624 onBeforeLoad : function(){
7626 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7631 destroy : function(){
7633 this.store.un('beforeload', this.onBeforeLoad, this);
7634 this.store.un('load', this.onLoad, this);
7635 this.store.un('loadexception', this.onLoadException, this);
7637 var um = this.el.getUpdateManager();
7638 um.un('beforeupdate', this.onBeforeLoad, this);
7639 um.un('update', this.onLoad, this);
7640 um.un('failure', this.onLoad, this);
7651 * @class Roo.bootstrap.Table
7652 * @extends Roo.bootstrap.Component
7653 * Bootstrap Table class
7654 * @cfg {String} cls table class
7655 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656 * @cfg {String} bgcolor Specifies the background color for a table
7657 * @cfg {Number} border Specifies whether the table cells should have borders or not
7658 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659 * @cfg {Number} cellspacing Specifies the space between cells
7660 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662 * @cfg {String} sortable Specifies that the table should be sortable
7663 * @cfg {String} summary Specifies a summary of the content of a table
7664 * @cfg {Number} width Specifies the width of a table
7665 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7667 * @cfg {boolean} striped Should the rows be alternative striped
7668 * @cfg {boolean} bordered Add borders to the table
7669 * @cfg {boolean} hover Add hover highlighting
7670 * @cfg {boolean} condensed Format condensed
7671 * @cfg {boolean} responsive Format condensed
7672 * @cfg {Boolean} loadMask (true|false) default false
7673 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675 * @cfg {Boolean} rowSelection (true|false) default false
7676 * @cfg {Boolean} cellSelection (true|false) default false
7677 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7679 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7680 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7684 * Create a new Table
7685 * @param {Object} config The config object
7688 Roo.bootstrap.Table = function(config){
7689 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7694 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7699 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7701 this.sm.grid = this;
7702 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703 this.sm = this.selModel;
7704 this.sm.xmodule = this.xmodule || false;
7707 if (this.cm && typeof(this.cm.config) == 'undefined') {
7708 this.colModel = new Roo.grid.ColumnModel(this.cm);
7709 this.cm = this.colModel;
7710 this.cm.xmodule = this.xmodule || false;
7713 this.store= Roo.factory(this.store, Roo.data);
7714 this.ds = this.store;
7715 this.ds.xmodule = this.xmodule || false;
7718 if (this.footer && this.store) {
7719 this.footer.dataSource = this.ds;
7720 this.footer = Roo.factory(this.footer);
7727 * Fires when a cell is clicked
7728 * @param {Roo.bootstrap.Table} this
7729 * @param {Roo.Element} el
7730 * @param {Number} rowIndex
7731 * @param {Number} columnIndex
7732 * @param {Roo.EventObject} e
7736 * @event celldblclick
7737 * Fires when a cell is double clicked
7738 * @param {Roo.bootstrap.Table} this
7739 * @param {Roo.Element} el
7740 * @param {Number} rowIndex
7741 * @param {Number} columnIndex
7742 * @param {Roo.EventObject} e
7744 "celldblclick" : true,
7747 * Fires when a row is clicked
7748 * @param {Roo.bootstrap.Table} this
7749 * @param {Roo.Element} el
7750 * @param {Number} rowIndex
7751 * @param {Roo.EventObject} e
7755 * @event rowdblclick
7756 * Fires when a row is double clicked
7757 * @param {Roo.bootstrap.Table} this
7758 * @param {Roo.Element} el
7759 * @param {Number} rowIndex
7760 * @param {Roo.EventObject} e
7762 "rowdblclick" : true,
7765 * Fires when a mouseover occur
7766 * @param {Roo.bootstrap.Table} this
7767 * @param {Roo.Element} el
7768 * @param {Number} rowIndex
7769 * @param {Number} columnIndex
7770 * @param {Roo.EventObject} e
7775 * Fires when a mouseout occur
7776 * @param {Roo.bootstrap.Table} this
7777 * @param {Roo.Element} el
7778 * @param {Number} rowIndex
7779 * @param {Number} columnIndex
7780 * @param {Roo.EventObject} e
7785 * Fires when a row is rendered, so you can change add a style to it.
7786 * @param {Roo.bootstrap.Table} this
7787 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7791 * @event rowsrendered
7792 * Fires when all the rows have been rendered
7793 * @param {Roo.bootstrap.Table} this
7795 'rowsrendered' : true,
7797 * @event contextmenu
7798 * The raw contextmenu event for the entire grid.
7799 * @param {Roo.EventObject} e
7801 "contextmenu" : true,
7803 * @event rowcontextmenu
7804 * Fires when a row is right clicked
7805 * @param {Roo.bootstrap.Table} this
7806 * @param {Number} rowIndex
7807 * @param {Roo.EventObject} e
7809 "rowcontextmenu" : true,
7811 * @event cellcontextmenu
7812 * Fires when a cell is right clicked
7813 * @param {Roo.bootstrap.Table} this
7814 * @param {Number} rowIndex
7815 * @param {Number} cellIndex
7816 * @param {Roo.EventObject} e
7818 "cellcontextmenu" : true,
7820 * @event headercontextmenu
7821 * Fires when a header is right clicked
7822 * @param {Roo.bootstrap.Table} this
7823 * @param {Number} columnIndex
7824 * @param {Roo.EventObject} e
7826 "headercontextmenu" : true
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7856 rowSelection : false,
7857 cellSelection : false,
7860 // Roo.Element - the tbody
7862 // Roo.Element - thead element
7865 container: false, // used by gridpanel...
7871 auto_hide_footer : false,
7873 getAutoCreate : function()
7875 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7882 if (this.scrollBody) {
7883 cfg.cls += ' table-body-fixed';
7886 cfg.cls += ' table-striped';
7890 cfg.cls += ' table-hover';
7892 if (this.bordered) {
7893 cfg.cls += ' table-bordered';
7895 if (this.condensed) {
7896 cfg.cls += ' table-condensed';
7898 if (this.responsive) {
7899 cfg.cls += ' table-responsive';
7903 cfg.cls+= ' ' +this.cls;
7906 // this lot should be simplifed...
7919 ].forEach(function(k) {
7927 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7930 if(this.store || this.cm){
7931 if(this.headerShow){
7932 cfg.cn.push(this.renderHeader());
7935 cfg.cn.push(this.renderBody());
7937 if(this.footerShow){
7938 cfg.cn.push(this.renderFooter());
7940 // where does this come from?
7941 //cfg.cls+= ' TableGrid';
7944 return { cn : [ cfg ] };
7947 initEvents : function()
7949 if(!this.store || !this.cm){
7952 if (this.selModel) {
7953 this.selModel.initEvents();
7957 //Roo.log('initEvents with ds!!!!');
7959 this.mainBody = this.el.select('tbody', true).first();
7960 this.mainHead = this.el.select('thead', true).first();
7961 this.mainFoot = this.el.select('tfoot', true).first();
7967 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968 e.on('click', _this.sort, _this);
7971 this.mainBody.on("click", this.onClick, this);
7972 this.mainBody.on("dblclick", this.onDblClick, this);
7974 // why is this done????? = it breaks dialogs??
7975 //this.parent().el.setStyle('position', 'relative');
7979 this.footer.parentId = this.id;
7980 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7983 this.el.select('tfoot tr td').first().addClass('hide');
7988 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7991 this.store.on('load', this.onLoad, this);
7992 this.store.on('beforeload', this.onBeforeLoad, this);
7993 this.store.on('update', this.onUpdate, this);
7994 this.store.on('add', this.onAdd, this);
7995 this.store.on("clear", this.clear, this);
7997 this.el.on("contextmenu", this.onContextMenu, this);
7999 this.mainBody.on('scroll', this.onBodyScroll, this);
8001 this.cm.on("headerchange", this.onHeaderChange, this);
8003 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8007 onContextMenu : function(e, t)
8009 this.processEvent("contextmenu", e);
8012 processEvent : function(name, e)
8014 if (name != 'touchstart' ) {
8015 this.fireEvent(name, e);
8018 var t = e.getTarget();
8020 var cell = Roo.get(t);
8026 if(cell.findParent('tfoot', false, true)){
8030 if(cell.findParent('thead', false, true)){
8032 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033 cell = Roo.get(t).findParent('th', false, true);
8035 Roo.log("failed to find th in thead?");
8036 Roo.log(e.getTarget());
8041 var cellIndex = cell.dom.cellIndex;
8043 var ename = name == 'touchstart' ? 'click' : name;
8044 this.fireEvent("header" + ename, this, cellIndex, e);
8049 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050 cell = Roo.get(t).findParent('td', false, true);
8052 Roo.log("failed to find th in tbody?");
8053 Roo.log(e.getTarget());
8058 var row = cell.findParent('tr', false, true);
8059 var cellIndex = cell.dom.cellIndex;
8060 var rowIndex = row.dom.rowIndex - 1;
8064 this.fireEvent("row" + name, this, rowIndex, e);
8068 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8074 onMouseover : function(e, el)
8076 var cell = Roo.get(el);
8082 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083 cell = cell.findParent('td', false, true);
8086 var row = cell.findParent('tr', false, true);
8087 var cellIndex = cell.dom.cellIndex;
8088 var rowIndex = row.dom.rowIndex - 1; // start from 0
8090 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8094 onMouseout : function(e, el)
8096 var cell = Roo.get(el);
8102 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103 cell = cell.findParent('td', false, true);
8106 var row = cell.findParent('tr', false, true);
8107 var cellIndex = cell.dom.cellIndex;
8108 var rowIndex = row.dom.rowIndex - 1; // start from 0
8110 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8114 onClick : function(e, el)
8116 var cell = Roo.get(el);
8118 if(!cell || (!this.cellSelection && !this.rowSelection)){
8122 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123 cell = cell.findParent('td', false, true);
8126 if(!cell || typeof(cell) == 'undefined'){
8130 var row = cell.findParent('tr', false, true);
8132 if(!row || typeof(row) == 'undefined'){
8136 var cellIndex = cell.dom.cellIndex;
8137 var rowIndex = this.getRowIndex(row);
8139 // why??? - should these not be based on SelectionModel?
8140 if(this.cellSelection){
8141 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8144 if(this.rowSelection){
8145 this.fireEvent('rowclick', this, row, rowIndex, e);
8151 onDblClick : function(e,el)
8153 var cell = Roo.get(el);
8155 if(!cell || (!this.cellSelection && !this.rowSelection)){
8159 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160 cell = cell.findParent('td', false, true);
8163 if(!cell || typeof(cell) == 'undefined'){
8167 var row = cell.findParent('tr', false, true);
8169 if(!row || typeof(row) == 'undefined'){
8173 var cellIndex = cell.dom.cellIndex;
8174 var rowIndex = this.getRowIndex(row);
8176 if(this.cellSelection){
8177 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8180 if(this.rowSelection){
8181 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8185 sort : function(e,el)
8187 var col = Roo.get(el);
8189 if(!col.hasClass('sortable')){
8193 var sort = col.attr('sort');
8196 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8200 this.store.sortInfo = {field : sort, direction : dir};
8203 Roo.log("calling footer first");
8204 this.footer.onClick('first');
8207 this.store.load({ params : { start : 0 } });
8211 renderHeader : function()
8219 this.totalWidth = 0;
8221 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8223 var config = cm.config[i];
8227 cls : 'x-hcol-' + i,
8229 html: cm.getColumnHeader(i)
8234 if(typeof(config.sortable) != 'undefined' && config.sortable){
8236 c.html = '<i class="glyphicon"></i>' + c.html;
8239 // could use BS4 hidden-..-down
8241 if(typeof(config.lgHeader) != 'undefined'){
8242 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8245 if(typeof(config.mdHeader) != 'undefined'){
8246 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8249 if(typeof(config.smHeader) != 'undefined'){
8250 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8253 if(typeof(config.xsHeader) != 'undefined'){
8254 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8261 if(typeof(config.tooltip) != 'undefined'){
8262 c.tooltip = config.tooltip;
8265 if(typeof(config.colspan) != 'undefined'){
8266 c.colspan = config.colspan;
8269 if(typeof(config.hidden) != 'undefined' && config.hidden){
8270 c.style += ' display:none;';
8273 if(typeof(config.dataIndex) != 'undefined'){
8274 c.sort = config.dataIndex;
8279 if(typeof(config.align) != 'undefined' && config.align.length){
8280 c.style += ' text-align:' + config.align + ';';
8283 if(typeof(config.width) != 'undefined'){
8284 c.style += ' width:' + config.width + 'px;';
8285 this.totalWidth += config.width;
8287 this.totalWidth += 100; // assume minimum of 100 per column?
8290 if(typeof(config.cls) != 'undefined'){
8291 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8294 ['xs','sm','md','lg'].map(function(size){
8296 if(typeof(config[size]) == 'undefined'){
8300 if (!config[size]) { // 0 = hidden
8301 // BS 4 '0' is treated as hide that column and below.
8302 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8306 c.cls += ' col-' + size + '-' + config[size] + (
8307 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8319 renderBody : function()
8329 colspan : this.cm.getColumnCount()
8339 renderFooter : function()
8349 colspan : this.cm.getColumnCount()
8363 // Roo.log('ds onload');
8368 var ds = this.store;
8370 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372 if (_this.store.sortInfo) {
8374 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375 e.select('i', true).addClass(['glyphicon-arrow-up']);
8378 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379 e.select('i', true).addClass(['glyphicon-arrow-down']);
8384 var tbody = this.mainBody;
8386 if(ds.getCount() > 0){
8387 ds.data.each(function(d,rowIndex){
8388 var row = this.renderRow(cm, ds, rowIndex);
8390 tbody.createChild(row);
8394 if(row.cellObjects.length){
8395 Roo.each(row.cellObjects, function(r){
8396 _this.renderCellObject(r);
8403 var tfoot = this.el.select('tfoot', true).first();
8405 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8407 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8409 var total = this.ds.getTotalCount();
8411 if(this.footer.pageSize < total){
8412 this.mainFoot.show();
8416 Roo.each(this.el.select('tbody td', true).elements, function(e){
8417 e.on('mouseover', _this.onMouseover, _this);
8420 Roo.each(this.el.select('tbody td', true).elements, function(e){
8421 e.on('mouseout', _this.onMouseout, _this);
8423 this.fireEvent('rowsrendered', this);
8429 onUpdate : function(ds,record)
8431 this.refreshRow(record);
8435 onRemove : function(ds, record, index, isUpdate){
8436 if(isUpdate !== true){
8437 this.fireEvent("beforerowremoved", this, index, record);
8439 var bt = this.mainBody.dom;
8441 var rows = this.el.select('tbody > tr', true).elements;
8443 if(typeof(rows[index]) != 'undefined'){
8444 bt.removeChild(rows[index].dom);
8447 // if(bt.rows[index]){
8448 // bt.removeChild(bt.rows[index]);
8451 if(isUpdate !== true){
8452 //this.stripeRows(index);
8453 //this.syncRowHeights(index, index);
8455 this.fireEvent("rowremoved", this, index, record);
8459 onAdd : function(ds, records, rowIndex)
8461 //Roo.log('on Add called');
8462 // - note this does not handle multiple adding very well..
8463 var bt = this.mainBody.dom;
8464 for (var i =0 ; i < records.length;i++) {
8465 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466 //Roo.log(records[i]);
8467 //Roo.log(this.store.getAt(rowIndex+i));
8468 this.insertRow(this.store, rowIndex + i, false);
8475 refreshRow : function(record){
8476 var ds = this.store, index;
8477 if(typeof record == 'number'){
8479 record = ds.getAt(index);
8481 index = ds.indexOf(record);
8483 return; // should not happen - but seems to
8486 this.insertRow(ds, index, true);
8488 this.onRemove(ds, record, index+1, true);
8490 //this.syncRowHeights(index, index);
8492 this.fireEvent("rowupdated", this, index, record);
8495 insertRow : function(dm, rowIndex, isUpdate){
8498 this.fireEvent("beforerowsinserted", this, rowIndex);
8500 //var s = this.getScrollState();
8501 var row = this.renderRow(this.cm, this.store, rowIndex);
8502 // insert before rowIndex..
8503 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8507 if(row.cellObjects.length){
8508 Roo.each(row.cellObjects, function(r){
8509 _this.renderCellObject(r);
8514 this.fireEvent("rowsinserted", this, rowIndex);
8515 //this.syncRowHeights(firstRow, lastRow);
8516 //this.stripeRows(firstRow);
8523 getRowDom : function(rowIndex)
8525 var rows = this.el.select('tbody > tr', true).elements;
8527 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8530 // returns the object tree for a tr..
8533 renderRow : function(cm, ds, rowIndex)
8535 var d = ds.getAt(rowIndex);
8539 cls : 'x-row-' + rowIndex,
8543 var cellObjects = [];
8545 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546 var config = cm.config[i];
8548 var renderer = cm.getRenderer(i);
8552 if(typeof(renderer) !== 'undefined'){
8553 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8555 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556 // and are rendered into the cells after the row is rendered - using the id for the element.
8558 if(typeof(value) === 'object'){
8568 rowIndex : rowIndex,
8573 this.fireEvent('rowclass', this, rowcfg);
8577 cls : rowcfg.rowClass + ' x-col-' + i,
8579 html: (typeof(value) === 'object') ? '' : value
8586 if(typeof(config.colspan) != 'undefined'){
8587 td.colspan = config.colspan;
8590 if(typeof(config.hidden) != 'undefined' && config.hidden){
8591 td.style += ' display:none;';
8594 if(typeof(config.align) != 'undefined' && config.align.length){
8595 td.style += ' text-align:' + config.align + ';';
8597 if(typeof(config.valign) != 'undefined' && config.valign.length){
8598 td.style += ' vertical-align:' + config.valign + ';';
8601 if(typeof(config.width) != 'undefined'){
8602 td.style += ' width:' + config.width + 'px;';
8605 if(typeof(config.cursor) != 'undefined'){
8606 td.style += ' cursor:' + config.cursor + ';';
8609 if(typeof(config.cls) != 'undefined'){
8610 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8613 ['xs','sm','md','lg'].map(function(size){
8615 if(typeof(config[size]) == 'undefined'){
8621 if (!config[size]) { // 0 = hidden
8622 // BS 4 '0' is treated as hide that column and below.
8623 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8627 td.cls += ' col-' + size + '-' + config[size] + (
8628 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8638 row.cellObjects = cellObjects;
8646 onBeforeLoad : function()
8655 this.el.select('tbody', true).first().dom.innerHTML = '';
8658 * Show or hide a row.
8659 * @param {Number} rowIndex to show or hide
8660 * @param {Boolean} state hide
8662 setRowVisibility : function(rowIndex, state)
8664 var bt = this.mainBody.dom;
8666 var rows = this.el.select('tbody > tr', true).elements;
8668 if(typeof(rows[rowIndex]) == 'undefined'){
8671 rows[rowIndex].dom.style.display = state ? '' : 'none';
8675 getSelectionModel : function(){
8677 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8679 return this.selModel;
8682 * Render the Roo.bootstrap object from renderder
8684 renderCellObject : function(r)
8688 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8690 var t = r.cfg.render(r.container);
8693 Roo.each(r.cfg.cn, function(c){
8695 container: t.getChildContainer(),
8698 _this.renderCellObject(child);
8703 getRowIndex : function(row)
8707 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8718 * Returns the grid's underlying element = used by panel.Grid
8719 * @return {Element} The element
8721 getGridEl : function(){
8725 * Forces a resize - used by panel.Grid
8726 * @return {Element} The element
8728 autoSize : function()
8730 //var ctr = Roo.get(this.container.dom.parentElement);
8731 var ctr = Roo.get(this.el.dom);
8733 var thd = this.getGridEl().select('thead',true).first();
8734 var tbd = this.getGridEl().select('tbody', true).first();
8735 var tfd = this.getGridEl().select('tfoot', true).first();
8737 var cw = ctr.getWidth();
8738 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8742 tbd.setWidth(ctr.getWidth());
8743 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744 // this needs fixing for various usage - currently only hydra job advers I think..
8746 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8748 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8751 cw = Math.max(cw, this.totalWidth);
8752 this.getGridEl().select('tbody tr',true).setWidth(cw);
8754 // resize 'expandable coloumn?
8756 return; // we doe not have a view in this design..
8759 onBodyScroll: function()
8761 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8763 this.mainHead.setStyle({
8764 'position' : 'relative',
8765 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8771 var scrollHeight = this.mainBody.dom.scrollHeight;
8773 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8775 var height = this.mainBody.getHeight();
8777 if(scrollHeight - height == scrollTop) {
8779 var total = this.ds.getTotalCount();
8781 if(this.footer.cursor + this.footer.pageSize < total){
8783 this.footer.ds.load({
8785 start : this.footer.cursor + this.footer.pageSize,
8786 limit : this.footer.pageSize
8796 onHeaderChange : function()
8798 var header = this.renderHeader();
8799 var table = this.el.select('table', true).first();
8801 this.mainHead.remove();
8802 this.mainHead = table.createChild(header, this.mainBody, false);
8805 onHiddenChange : function(colModel, colIndex, hidden)
8807 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8810 this.CSS.updateRule(thSelector, "display", "");
8811 this.CSS.updateRule(tdSelector, "display", "");
8814 this.CSS.updateRule(thSelector, "display", "none");
8815 this.CSS.updateRule(tdSelector, "display", "none");
8818 this.onHeaderChange();
8822 setColumnWidth: function(col_index, width)
8824 // width = "md-2 xs-2..."
8825 if(!this.colModel.config[col_index]) {
8829 var w = width.split(" ");
8831 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8833 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8836 for(var j = 0; j < w.length; j++) {
8842 var size_cls = w[j].split("-");
8844 if(!Number.isInteger(size_cls[1] * 1)) {
8848 if(!this.colModel.config[col_index][size_cls[0]]) {
8852 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8856 h_row[0].classList.replace(
8857 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858 "col-"+size_cls[0]+"-"+size_cls[1]
8861 for(var i = 0; i < rows.length; i++) {
8863 var size_cls = w[j].split("-");
8865 if(!Number.isInteger(size_cls[1] * 1)) {
8869 if(!this.colModel.config[col_index][size_cls[0]]) {
8873 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877 rows[i].classList.replace(
8878 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879 "col-"+size_cls[0]+"-"+size_cls[1]
8883 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8898 * @class Roo.bootstrap.TableCell
8899 * @extends Roo.bootstrap.Component
8900 * Bootstrap TableCell class
8901 * @cfg {String} html cell contain text
8902 * @cfg {String} cls cell class
8903 * @cfg {String} tag cell tag (td|th) default td
8904 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905 * @cfg {String} align Aligns the content in a cell
8906 * @cfg {String} axis Categorizes cells
8907 * @cfg {String} bgcolor Specifies the background color of a cell
8908 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909 * @cfg {Number} colspan Specifies the number of columns a cell should span
8910 * @cfg {String} headers Specifies one or more header cells a cell is related to
8911 * @cfg {Number} height Sets the height of a cell
8912 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913 * @cfg {Number} rowspan Sets the number of rows a cell should span
8914 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915 * @cfg {String} valign Vertical aligns the content in a cell
8916 * @cfg {Number} width Specifies the width of a cell
8919 * Create a new TableCell
8920 * @param {Object} config The config object
8923 Roo.bootstrap.TableCell = function(config){
8924 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8947 getAutoCreate : function(){
8948 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8968 cfg.align=this.align
8974 cfg.bgcolor=this.bgcolor
8977 cfg.charoff=this.charoff
8980 cfg.colspan=this.colspan
8983 cfg.headers=this.headers
8986 cfg.height=this.height
8989 cfg.nowrap=this.nowrap
8992 cfg.rowspan=this.rowspan
8995 cfg.scope=this.scope
8998 cfg.valign=this.valign
9001 cfg.width=this.width
9020 * @class Roo.bootstrap.TableRow
9021 * @extends Roo.bootstrap.Component
9022 * Bootstrap TableRow class
9023 * @cfg {String} cls row class
9024 * @cfg {String} align Aligns the content in a table row
9025 * @cfg {String} bgcolor Specifies a background color for a table row
9026 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027 * @cfg {String} valign Vertical aligns the content in a table row
9030 * Create a new TableRow
9031 * @param {Object} config The config object
9034 Roo.bootstrap.TableRow = function(config){
9035 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9046 getAutoCreate : function(){
9047 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9057 cfg.align = this.align;
9060 cfg.bgcolor = this.bgcolor;
9063 cfg.charoff = this.charoff;
9066 cfg.valign = this.valign;
9084 * @class Roo.bootstrap.TableBody
9085 * @extends Roo.bootstrap.Component
9086 * Bootstrap TableBody class
9087 * @cfg {String} cls element class
9088 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089 * @cfg {String} align Aligns the content inside the element
9090 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9094 * Create a new TableBody
9095 * @param {Object} config The config object
9098 Roo.bootstrap.TableBody = function(config){
9099 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9110 getAutoCreate : function(){
9111 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9125 cfg.align = this.align;
9128 cfg.charoff = this.charoff;
9131 cfg.valign = this.valign;
9138 // initEvents : function()
9145 // this.store = Roo.factory(this.store, Roo.data);
9146 // this.store.on('load', this.onLoad, this);
9148 // this.store.load();
9152 // onLoad: function ()
9154 // this.fireEvent('load', this);
9164 * Ext JS Library 1.1.1
9165 * Copyright(c) 2006-2007, Ext JS, LLC.
9167 * Originally Released Under LGPL - original licence link has changed is not relivant.
9170 * <script type="text/javascript">
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9176 * @class Roo.form.Action
9177 * Internal Class used to handle form actions
9179 * @param {Roo.form.BasicForm} el The form element or its id
9180 * @param {Object} config Configuration options
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9188 this.options = options || {};
9191 * Client Validation Failed
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9196 * Server Validation Failed
9199 Roo.form.Action.SERVER_INVALID = 'server';
9201 * Connect to Server Failed
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9206 * Reading Data from Server Failed
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9211 Roo.form.Action.prototype = {
9213 failureType : undefined,
9214 response : undefined,
9218 run : function(options){
9223 success : function(response){
9228 handleResponse : function(response){
9232 // default connection failure
9233 failure : function(response){
9235 this.response = response;
9236 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237 this.form.afterAction(this, false);
9240 processResponse : function(response){
9241 this.response = response;
9242 if(!response.responseText){
9245 this.result = this.handleResponse(response);
9249 // utility functions used internally
9250 getUrl : function(appendParams){
9251 var url = this.options.url || this.form.url || this.form.el.dom.action;
9253 var p = this.getParams();
9255 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9261 getMethod : function(){
9262 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9265 getParams : function(){
9266 var bp = this.form.baseParams;
9267 var p = this.options.params;
9269 if(typeof p == "object"){
9270 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271 }else if(typeof p == 'string' && bp){
9272 p += '&' + Roo.urlEncode(bp);
9275 p = Roo.urlEncode(bp);
9280 createCallback : function(){
9282 success: this.success,
9283 failure: this.failure,
9285 timeout: (this.form.timeout*1000),
9286 upload: this.form.fileUpload ? this.success : undefined
9291 Roo.form.Action.Submit = function(form, options){
9292 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9298 haveProgress : false,
9299 uploadComplete : false,
9301 // uploadProgress indicator.
9302 uploadProgress : function()
9304 if (!this.form.progressUrl) {
9308 if (!this.haveProgress) {
9309 Roo.MessageBox.progress("Uploading", "Uploading");
9311 if (this.uploadComplete) {
9312 Roo.MessageBox.hide();
9316 this.haveProgress = true;
9318 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9320 var c = new Roo.data.Connection();
9322 url : this.form.progressUrl,
9327 success : function(req){
9328 //console.log(data);
9332 rdata = Roo.decode(req.responseText)
9334 Roo.log("Invalid data from server..");
9338 if (!rdata || !rdata.success) {
9340 Roo.MessageBox.alert(Roo.encode(rdata));
9343 var data = rdata.data;
9345 if (this.uploadComplete) {
9346 Roo.MessageBox.hide();
9351 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9355 this.uploadProgress.defer(2000,this);
9358 failure: function(data) {
9359 Roo.log('progress url failed ');
9370 // run get Values on the form, so it syncs any secondary forms.
9371 this.form.getValues();
9373 var o = this.options;
9374 var method = this.getMethod();
9375 var isPost = method == 'POST';
9376 if(o.clientValidation === false || this.form.isValid()){
9378 if (this.form.progressUrl) {
9379 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380 (new Date() * 1) + '' + Math.random());
9385 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386 form:this.form.el.dom,
9387 url:this.getUrl(!isPost),
9389 params:isPost ? this.getParams() : null,
9390 isUpload: this.form.fileUpload,
9391 formData : this.form.formData
9394 this.uploadProgress();
9396 }else if (o.clientValidation !== false){ // client validation failed
9397 this.failureType = Roo.form.Action.CLIENT_INVALID;
9398 this.form.afterAction(this, false);
9402 success : function(response)
9404 this.uploadComplete= true;
9405 if (this.haveProgress) {
9406 Roo.MessageBox.hide();
9410 var result = this.processResponse(response);
9411 if(result === true || result.success){
9412 this.form.afterAction(this, true);
9416 this.form.markInvalid(result.errors);
9417 this.failureType = Roo.form.Action.SERVER_INVALID;
9419 this.form.afterAction(this, false);
9421 failure : function(response)
9423 this.uploadComplete= true;
9424 if (this.haveProgress) {
9425 Roo.MessageBox.hide();
9428 this.response = response;
9429 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430 this.form.afterAction(this, false);
9433 handleResponse : function(response){
9434 if(this.form.errorReader){
9435 var rs = this.form.errorReader.read(response);
9438 for(var i = 0, len = rs.records.length; i < len; i++) {
9439 var r = rs.records[i];
9443 if(errors.length < 1){
9447 success : rs.success,
9453 ret = Roo.decode(response.responseText);
9457 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9467 Roo.form.Action.Load = function(form, options){
9468 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469 this.reader = this.form.reader;
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9477 Roo.Ajax.request(Roo.apply(
9478 this.createCallback(), {
9479 method:this.getMethod(),
9480 url:this.getUrl(false),
9481 params:this.getParams()
9485 success : function(response){
9487 var result = this.processResponse(response);
9488 if(result === true || !result.success || !result.data){
9489 this.failureType = Roo.form.Action.LOAD_FAILURE;
9490 this.form.afterAction(this, false);
9493 this.form.clearInvalid();
9494 this.form.setValues(result.data);
9495 this.form.afterAction(this, true);
9498 handleResponse : function(response){
9499 if(this.form.reader){
9500 var rs = this.form.reader.read(response);
9501 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9503 success : rs.success,
9507 return Roo.decode(response.responseText);
9511 Roo.form.Action.ACTION_TYPES = {
9512 'load' : Roo.form.Action.Load,
9513 'submit' : Roo.form.Action.Submit
9522 * @class Roo.bootstrap.Form
9523 * @extends Roo.bootstrap.Component
9524 * Bootstrap Form class
9525 * @cfg {String} method GET | POST (default POST)
9526 * @cfg {String} labelAlign top | left (default top)
9527 * @cfg {String} align left | right - for navbars
9528 * @cfg {Boolean} loadMask load mask when submit (default true)
9533 * @param {Object} config The config object
9537 Roo.bootstrap.Form = function(config){
9539 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9541 Roo.bootstrap.Form.popover.apply();
9545 * @event clientvalidation
9546 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547 * @param {Form} this
9548 * @param {Boolean} valid true if the form has passed client-side validation
9550 clientvalidation: true,
9552 * @event beforeaction
9553 * Fires before any action is performed. Return false to cancel the action.
9554 * @param {Form} this
9555 * @param {Action} action The action to be performed
9559 * @event actionfailed
9560 * Fires when an action fails.
9561 * @param {Form} this
9562 * @param {Action} action The action that failed
9564 actionfailed : true,
9566 * @event actioncomplete
9567 * Fires when an action is completed.
9568 * @param {Form} this
9569 * @param {Action} action The action that completed
9571 actioncomplete : true
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9578 * @cfg {String} method
9579 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9584 * The URL to use for form actions if one isn't supplied in the action options.
9587 * @cfg {Boolean} fileUpload
9588 * Set to true if this form is a file upload.
9592 * @cfg {Object} baseParams
9593 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9597 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9601 * @cfg {Sting} align (left|right) for navbar forms
9606 activeAction : null,
9609 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610 * element by passing it or its id or mask the form itself by passing in true.
9613 waitMsgTarget : false,
9618 * @cfg {Boolean} errorMask (true|false) default false
9623 * @cfg {Number} maskOffset Default 100
9628 * @cfg {Boolean} maskBody
9632 getAutoCreate : function(){
9636 method : this.method || 'POST',
9637 id : this.id || Roo.id(),
9640 if (this.parent().xtype.match(/^Nav/)) {
9641 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9645 if (this.labelAlign == 'left' ) {
9646 cfg.cls += ' form-horizontal';
9652 initEvents : function()
9654 this.el.on('submit', this.onSubmit, this);
9655 // this was added as random key presses on the form where triggering form submit.
9656 this.el.on('keypress', function(e) {
9657 if (e.getCharCode() != 13) {
9660 // we might need to allow it for textareas.. and some other items.
9661 // check e.getTarget().
9663 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9667 Roo.log("keypress blocked");
9675 onSubmit : function(e){
9680 * Returns true if client-side validation on the form is successful.
9683 isValid : function(){
9684 var items = this.getItems();
9688 items.each(function(f){
9694 Roo.log('invalid field: ' + f.name);
9698 if(!target && f.el.isVisible(true)){
9704 if(this.errorMask && !valid){
9705 Roo.bootstrap.Form.popover.mask(this, target);
9712 * Returns true if any fields in this form have changed since their original load.
9715 isDirty : function(){
9717 var items = this.getItems();
9718 items.each(function(f){
9728 * Performs a predefined action (submit or load) or custom actions you define on this form.
9729 * @param {String} actionName The name of the action type
9730 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9731 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732 * accept other config options):
9734 Property Type Description
9735 ---------------- --------------- ----------------------------------------------------------------------------------
9736 url String The url for the action (defaults to the form's url)
9737 method String The form method to use (defaults to the form's method, or POST if not defined)
9738 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9740 validate the form on the client (defaults to false)
9742 * @return {BasicForm} this
9744 doAction : function(action, options){
9745 if(typeof action == 'string'){
9746 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9748 if(this.fireEvent('beforeaction', this, action) !== false){
9749 this.beforeAction(action);
9750 action.run.defer(100, action);
9756 beforeAction : function(action){
9757 var o = action.options;
9762 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9764 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9767 // not really supported yet.. ??
9769 //if(this.waitMsgTarget === true){
9770 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771 //}else if(this.waitMsgTarget){
9772 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9775 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9781 afterAction : function(action, success){
9782 this.activeAction = null;
9783 var o = action.options;
9788 Roo.get(document.body).unmask();
9794 //if(this.waitMsgTarget === true){
9795 // this.el.unmask();
9796 //}else if(this.waitMsgTarget){
9797 // this.waitMsgTarget.unmask();
9799 // Roo.MessageBox.updateProgress(1);
9800 // Roo.MessageBox.hide();
9807 Roo.callback(o.success, o.scope, [this, action]);
9808 this.fireEvent('actioncomplete', this, action);
9812 // failure condition..
9813 // we have a scenario where updates need confirming.
9814 // eg. if a locking scenario exists..
9815 // we look for { errors : { needs_confirm : true }} in the response.
9817 (typeof(action.result) != 'undefined') &&
9818 (typeof(action.result.errors) != 'undefined') &&
9819 (typeof(action.result.errors.needs_confirm) != 'undefined')
9822 Roo.log("not supported yet");
9825 Roo.MessageBox.confirm(
9826 "Change requires confirmation",
9827 action.result.errorMsg,
9832 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9842 Roo.callback(o.failure, o.scope, [this, action]);
9843 // show an error message if no failed handler is set..
9844 if (!this.hasListener('actionfailed')) {
9845 Roo.log("need to add dialog support");
9847 Roo.MessageBox.alert("Error",
9848 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849 action.result.errorMsg :
9850 "Saving Failed, please check your entries or try again"
9855 this.fireEvent('actionfailed', this, action);
9860 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861 * @param {String} id The value to search for
9864 findField : function(id){
9865 var items = this.getItems();
9866 var field = items.get(id);
9868 items.each(function(f){
9869 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9876 return field || null;
9879 * Mark fields in this form invalid in bulk.
9880 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881 * @return {BasicForm} this
9883 markInvalid : function(errors){
9884 if(errors instanceof Array){
9885 for(var i = 0, len = errors.length; i < len; i++){
9886 var fieldError = errors[i];
9887 var f = this.findField(fieldError.id);
9889 f.markInvalid(fieldError.msg);
9895 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896 field.markInvalid(errors[id]);
9900 //Roo.each(this.childForms || [], function (f) {
9901 // f.markInvalid(errors);
9908 * Set values for fields in this form in bulk.
9909 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910 * @return {BasicForm} this
9912 setValues : function(values){
9913 if(values instanceof Array){ // array of objects
9914 for(var i = 0, len = values.length; i < len; i++){
9916 var f = this.findField(v.id);
9918 f.setValue(v.value);
9919 if(this.trackResetOnLoad){
9920 f.originalValue = f.getValue();
9924 }else{ // object hash
9927 if(typeof values[id] != 'function' && (field = this.findField(id))){
9929 if (field.setFromData &&
9931 field.displayField &&
9932 // combos' with local stores can
9933 // be queried via setValue()
9934 // to set their value..
9935 (field.store && !field.store.isLocal)
9939 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941 field.setFromData(sd);
9943 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9945 field.setFromData(values);
9948 field.setValue(values[id]);
9952 if(this.trackResetOnLoad){
9953 field.originalValue = field.getValue();
9959 //Roo.each(this.childForms || [], function (f) {
9960 // f.setValues(values);
9967 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968 * they are returned as an array.
9969 * @param {Boolean} asString
9972 getValues : function(asString){
9973 //if (this.childForms) {
9974 // copy values from the child forms
9975 // Roo.each(this.childForms, function (f) {
9976 // this.setValues(f.getValues());
9982 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983 if(asString === true){
9986 return Roo.urlDecode(fs);
9990 * Returns the fields in this form as an object with key/value pairs.
9991 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9994 getFieldValues : function(with_hidden)
9996 var items = this.getItems();
9998 items.each(function(f){
10000 if (!f.getName()) {
10004 var v = f.getValue();
10006 if (f.inputType =='radio') {
10007 if (typeof(ret[f.getName()]) == 'undefined') {
10008 ret[f.getName()] = ''; // empty..
10011 if (!f.el.dom.checked) {
10015 v = f.el.dom.value;
10019 if(f.xtype == 'MoneyField'){
10020 ret[f.currencyName] = f.getCurrency();
10023 // not sure if this supported any more..
10024 if ((typeof(v) == 'object') && f.getRawValue) {
10025 v = f.getRawValue() ; // dates..
10027 // combo boxes where name != hiddenName...
10028 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029 ret[f.name] = f.getRawValue();
10031 ret[f.getName()] = v;
10038 * Clears all invalid messages in this form.
10039 * @return {BasicForm} this
10041 clearInvalid : function(){
10042 var items = this.getItems();
10044 items.each(function(f){
10052 * Resets this form.
10053 * @return {BasicForm} this
10055 reset : function(){
10056 var items = this.getItems();
10057 items.each(function(f){
10061 Roo.each(this.childForms || [], function (f) {
10069 getItems : function()
10071 var r=new Roo.util.MixedCollection(false, function(o){
10072 return o.id || (o.id = Roo.id());
10074 var iter = function(el) {
10081 Roo.each(el.items,function(e) {
10090 hideFields : function(items)
10092 Roo.each(items, function(i){
10094 var f = this.findField(i);
10105 showFields : function(items)
10107 Roo.each(items, function(i){
10109 var f = this.findField(i);
10122 Roo.apply(Roo.bootstrap.Form, {
10138 intervalID : false,
10144 if(this.isApplied){
10149 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10155 this.maskEl.top.enableDisplayMode("block");
10156 this.maskEl.left.enableDisplayMode("block");
10157 this.maskEl.bottom.enableDisplayMode("block");
10158 this.maskEl.right.enableDisplayMode("block");
10160 this.toolTip = new Roo.bootstrap.Tooltip({
10161 cls : 'roo-form-error-popover',
10163 'left' : ['r-l', [-2,0], 'right'],
10164 'right' : ['l-r', [2,0], 'left'],
10165 'bottom' : ['tl-bl', [0,2], 'top'],
10166 'top' : [ 'bl-tl', [0,-2], 'bottom']
10170 this.toolTip.render(Roo.get(document.body));
10172 this.toolTip.el.enableDisplayMode("block");
10174 Roo.get(document.body).on('click', function(){
10178 Roo.get(document.body).on('touchstart', function(){
10182 this.isApplied = true
10185 mask : function(form, target)
10189 this.target = target;
10191 if(!this.form.errorMask || !target.el){
10195 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10197 Roo.log(scrollable);
10199 var ot = this.target.el.calcOffsetsTo(scrollable);
10201 var scrollTo = ot[1] - this.form.maskOffset;
10203 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10205 scrollable.scrollTo('top', scrollTo);
10207 var box = this.target.el.getBox();
10209 var zIndex = Roo.bootstrap.Modal.zIndex++;
10212 this.maskEl.top.setStyle('position', 'absolute');
10213 this.maskEl.top.setStyle('z-index', zIndex);
10214 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215 this.maskEl.top.setLeft(0);
10216 this.maskEl.top.setTop(0);
10217 this.maskEl.top.show();
10219 this.maskEl.left.setStyle('position', 'absolute');
10220 this.maskEl.left.setStyle('z-index', zIndex);
10221 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222 this.maskEl.left.setLeft(0);
10223 this.maskEl.left.setTop(box.y - this.padding);
10224 this.maskEl.left.show();
10226 this.maskEl.bottom.setStyle('position', 'absolute');
10227 this.maskEl.bottom.setStyle('z-index', zIndex);
10228 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229 this.maskEl.bottom.setLeft(0);
10230 this.maskEl.bottom.setTop(box.bottom + this.padding);
10231 this.maskEl.bottom.show();
10233 this.maskEl.right.setStyle('position', 'absolute');
10234 this.maskEl.right.setStyle('z-index', zIndex);
10235 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236 this.maskEl.right.setLeft(box.right + this.padding);
10237 this.maskEl.right.setTop(box.y - this.padding);
10238 this.maskEl.right.show();
10240 this.toolTip.bindEl = this.target.el;
10242 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10244 var tip = this.target.blankText;
10246 if(this.target.getValue() !== '' ) {
10248 if (this.target.invalidText.length) {
10249 tip = this.target.invalidText;
10250 } else if (this.target.regexText.length){
10251 tip = this.target.regexText;
10255 this.toolTip.show(tip);
10257 this.intervalID = window.setInterval(function() {
10258 Roo.bootstrap.Form.popover.unmask();
10261 window.onwheel = function(){ return false;};
10263 (function(){ this.isMasked = true; }).defer(500, this);
10267 unmask : function()
10269 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10273 this.maskEl.top.setStyle('position', 'absolute');
10274 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275 this.maskEl.top.hide();
10277 this.maskEl.left.setStyle('position', 'absolute');
10278 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279 this.maskEl.left.hide();
10281 this.maskEl.bottom.setStyle('position', 'absolute');
10282 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283 this.maskEl.bottom.hide();
10285 this.maskEl.right.setStyle('position', 'absolute');
10286 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287 this.maskEl.right.hide();
10289 this.toolTip.hide();
10291 this.toolTip.el.hide();
10293 window.onwheel = function(){ return true;};
10295 if(this.intervalID){
10296 window.clearInterval(this.intervalID);
10297 this.intervalID = false;
10300 this.isMasked = false;
10310 * Ext JS Library 1.1.1
10311 * Copyright(c) 2006-2007, Ext JS, LLC.
10313 * Originally Released Under LGPL - original licence link has changed is not relivant.
10316 * <script type="text/javascript">
10319 * @class Roo.form.VTypes
10320 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10323 Roo.form.VTypes = function(){
10324 // closure these in so they are only created once.
10325 var alpha = /^[a-zA-Z_]+$/;
10326 var alphanum = /^[a-zA-Z0-9_]+$/;
10327 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10330 // All these messages and functions are configurable
10333 * The function used to validate email addresses
10334 * @param {String} value The email address
10336 'email' : function(v){
10337 return email.test(v);
10340 * The error text to display when the email validation function returns false
10343 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10345 * The keystroke filter mask to be applied on email input
10348 'emailMask' : /[a-z0-9_\.\-@]/i,
10351 * The function used to validate URLs
10352 * @param {String} value The URL
10354 'url' : function(v){
10355 return url.test(v);
10358 * The error text to display when the url validation function returns false
10361 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10364 * The function used to validate alpha values
10365 * @param {String} value The value
10367 'alpha' : function(v){
10368 return alpha.test(v);
10371 * The error text to display when the alpha validation function returns false
10374 'alphaText' : 'This field should only contain letters and _',
10376 * The keystroke filter mask to be applied on alpha input
10379 'alphaMask' : /[a-z_]/i,
10382 * The function used to validate alphanumeric values
10383 * @param {String} value The value
10385 'alphanum' : function(v){
10386 return alphanum.test(v);
10389 * The error text to display when the alphanumeric validation function returns false
10392 'alphanumText' : 'This field should only contain letters, numbers and _',
10394 * The keystroke filter mask to be applied on alphanumeric input
10397 'alphanumMask' : /[a-z0-9_]/i
10407 * @class Roo.bootstrap.Input
10408 * @extends Roo.bootstrap.Component
10409 * Bootstrap Input class
10410 * @cfg {Boolean} disabled is it disabled
10411 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10412 * @cfg {String} name name of the input
10413 * @cfg {string} fieldLabel - the label associated
10414 * @cfg {string} placeholder - placeholder to put in text.
10415 * @cfg {string} before - input group add on before
10416 * @cfg {string} after - input group add on after
10417 * @cfg {string} size - (lg|sm) or leave empty..
10418 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420 * @cfg {Number} md colspan out of 12 for computer-sized screens
10421 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422 * @cfg {string} value default value of the input
10423 * @cfg {Number} labelWidth set the width of label
10424 * @cfg {Number} labellg set the width of label (1-12)
10425 * @cfg {Number} labelmd set the width of label (1-12)
10426 * @cfg {Number} labelsm set the width of label (1-12)
10427 * @cfg {Number} labelxs set the width of label (1-12)
10428 * @cfg {String} labelAlign (top|left)
10429 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431 * @cfg {String} indicatorpos (left|right) default left
10432 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10436 * @cfg {String} align (left|center|right) Default left
10437 * @cfg {Boolean} forceFeedback (true|false) Default false
10440 * Create a new Input
10441 * @param {Object} config The config object
10444 Roo.bootstrap.Input = function(config){
10446 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10451 * Fires when this field receives input focus.
10452 * @param {Roo.form.Field} this
10457 * Fires when this field loses input focus.
10458 * @param {Roo.form.Field} this
10462 * @event specialkey
10463 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10464 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465 * @param {Roo.form.Field} this
10466 * @param {Roo.EventObject} e The event object
10471 * Fires just before the field blurs if the field value has changed.
10472 * @param {Roo.form.Field} this
10473 * @param {Mixed} newValue The new value
10474 * @param {Mixed} oldValue The original value
10479 * Fires after the field has been marked as invalid.
10480 * @param {Roo.form.Field} this
10481 * @param {String} msg The validation message
10486 * Fires after the field has been validated with no errors.
10487 * @param {Roo.form.Field} this
10492 * Fires after the key up
10493 * @param {Roo.form.Field} this
10494 * @param {Roo.EventObject} e The event Object
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10502 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503 automatic validation (defaults to "keyup").
10505 validationEvent : "keyup",
10507 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10509 validateOnBlur : true,
10511 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10513 validationDelay : 250,
10515 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10517 focusClass : "x-form-focus", // not needed???
10521 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10523 invalidClass : "has-warning",
10526 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10528 validClass : "has-success",
10531 * @cfg {Boolean} hasFeedback (true|false) default true
10533 hasFeedback : true,
10536 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10538 invalidFeedbackClass : "glyphicon-warning-sign",
10541 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10543 validFeedbackClass : "glyphicon-ok",
10546 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10548 selectOnFocus : false,
10551 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10555 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10560 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10562 disableKeyFilter : false,
10565 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10569 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10573 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10575 blankText : "Please complete this mandatory field",
10578 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10582 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10584 maxLength : Number.MAX_VALUE,
10586 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10588 minLengthText : "The minimum length for this field is {0}",
10590 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10592 maxLengthText : "The maximum length for this field is {0}",
10596 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597 * If available, this function will be called only after the basic validators all return true, and will be passed the
10598 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10602 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10608 * @cfg {String} regexText -- Depricated - use Invalid Text
10613 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10619 autocomplete: false,
10623 inputType : 'text',
10626 placeholder: false,
10631 preventMark: false,
10632 isFormField : true,
10635 labelAlign : false,
10638 formatedValue : false,
10639 forceFeedback : false,
10641 indicatorpos : 'left',
10651 parentLabelAlign : function()
10654 while (parent.parent()) {
10655 parent = parent.parent();
10656 if (typeof(parent.labelAlign) !='undefined') {
10657 return parent.labelAlign;
10664 getAutoCreate : function()
10666 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10672 if(this.inputType != 'hidden'){
10673 cfg.cls = 'form-group' //input-group
10679 type : this.inputType,
10680 value : this.value,
10681 cls : 'form-control',
10682 placeholder : this.placeholder || '',
10683 autocomplete : this.autocomplete || 'new-password'
10685 if (this.inputType == 'file') {
10686 input.style = 'overflow:hidden'; // why not in CSS?
10689 if(this.capture.length){
10690 input.capture = this.capture;
10693 if(this.accept.length){
10694 input.accept = this.accept + "/*";
10698 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10701 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702 input.maxLength = this.maxLength;
10705 if (this.disabled) {
10706 input.disabled=true;
10709 if (this.readOnly) {
10710 input.readonly=true;
10714 input.name = this.name;
10718 input.cls += ' input-' + this.size;
10722 ['xs','sm','md','lg'].map(function(size){
10723 if (settings[size]) {
10724 cfg.cls += ' col-' + size + '-' + settings[size];
10728 var inputblock = input;
10732 cls: 'glyphicon form-control-feedback'
10735 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10738 cls : 'has-feedback',
10746 if (this.before || this.after) {
10749 cls : 'input-group',
10753 if (this.before && typeof(this.before) == 'string') {
10755 inputblock.cn.push({
10757 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10761 if (this.before && typeof(this.before) == 'object') {
10762 this.before = Roo.factory(this.before);
10764 inputblock.cn.push({
10766 cls : 'roo-input-before input-group-prepend input-group-' +
10767 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10771 inputblock.cn.push(input);
10773 if (this.after && typeof(this.after) == 'string') {
10774 inputblock.cn.push({
10776 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10780 if (this.after && typeof(this.after) == 'object') {
10781 this.after = Roo.factory(this.after);
10783 inputblock.cn.push({
10785 cls : 'roo-input-after input-group-append input-group-' +
10786 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791 inputblock.cls += ' has-feedback';
10792 inputblock.cn.push(feedback);
10797 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798 tooltip : 'This field is required'
10800 if (this.allowBlank ) {
10801 indicator.style = this.allowBlank ? ' display:none' : '';
10803 if (align ==='left' && this.fieldLabel.length) {
10805 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10812 cls : 'control-label col-form-label',
10813 html : this.fieldLabel
10824 var labelCfg = cfg.cn[1];
10825 var contentCfg = cfg.cn[2];
10827 if(this.indicatorpos == 'right'){
10832 cls : 'control-label col-form-label',
10836 html : this.fieldLabel
10850 labelCfg = cfg.cn[0];
10851 contentCfg = cfg.cn[1];
10855 if(this.labelWidth > 12){
10856 labelCfg.style = "width: " + this.labelWidth + 'px';
10859 if(this.labelWidth < 13 && this.labelmd == 0){
10860 this.labelmd = this.labelWidth;
10863 if(this.labellg > 0){
10864 labelCfg.cls += ' col-lg-' + this.labellg;
10865 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10868 if(this.labelmd > 0){
10869 labelCfg.cls += ' col-md-' + this.labelmd;
10870 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10873 if(this.labelsm > 0){
10874 labelCfg.cls += ' col-sm-' + this.labelsm;
10875 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10878 if(this.labelxs > 0){
10879 labelCfg.cls += ' col-xs-' + this.labelxs;
10880 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10884 } else if ( this.fieldLabel.length) {
10891 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892 tooltip : 'This field is required',
10893 style : this.allowBlank ? ' display:none' : ''
10897 //cls : 'input-group-addon',
10898 html : this.fieldLabel
10906 if(this.indicatorpos == 'right'){
10911 //cls : 'input-group-addon',
10912 html : this.fieldLabel
10917 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918 tooltip : 'This field is required',
10919 style : this.allowBlank ? ' display:none' : ''
10939 if (this.parentType === 'Navbar' && this.parent().bar) {
10940 cfg.cls += ' navbar-form';
10943 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944 // on BS4 we do this only if not form
10945 cfg.cls += ' navbar-form';
10953 * return the real input element.
10955 inputEl: function ()
10957 return this.el.select('input.form-control',true).first();
10960 tooltipEl : function()
10962 return this.inputEl();
10965 indicatorEl : function()
10967 if (Roo.bootstrap.version == 4) {
10968 return false; // not enabled in v4 yet.
10971 var indicator = this.el.select('i.roo-required-indicator',true).first();
10981 setDisabled : function(v)
10983 var i = this.inputEl().dom;
10985 i.removeAttribute('disabled');
10989 i.setAttribute('disabled','true');
10991 initEvents : function()
10994 this.inputEl().on("keydown" , this.fireKey, this);
10995 this.inputEl().on("focus", this.onFocus, this);
10996 this.inputEl().on("blur", this.onBlur, this);
10998 this.inputEl().relayEvent('keyup', this);
11000 this.indicator = this.indicatorEl();
11002 if(this.indicator){
11003 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11006 // reference to original value for reset
11007 this.originalValue = this.getValue();
11008 //Roo.form.TextField.superclass.initEvents.call(this);
11009 if(this.validationEvent == 'keyup'){
11010 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011 this.inputEl().on('keyup', this.filterValidation, this);
11013 else if(this.validationEvent !== false){
11014 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11017 if(this.selectOnFocus){
11018 this.on("focus", this.preFocus, this);
11021 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022 this.inputEl().on("keypress", this.filterKeys, this);
11024 this.inputEl().relayEvent('keypress', this);
11027 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11028 this.el.on("click", this.autoSize, this);
11031 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11035 if (typeof(this.before) == 'object') {
11036 this.before.render(this.el.select('.roo-input-before',true).first());
11038 if (typeof(this.after) == 'object') {
11039 this.after.render(this.el.select('.roo-input-after',true).first());
11042 this.inputEl().on('change', this.onChange, this);
11045 filterValidation : function(e){
11046 if(!e.isNavKeyPress()){
11047 this.validationTask.delay(this.validationDelay);
11051 * Validates the field value
11052 * @return {Boolean} True if the value is valid, else false
11054 validate : function(){
11055 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056 if(this.disabled || this.validateValue(this.getRawValue())){
11061 this.markInvalid();
11067 * Validates a value according to the field's validation rules and marks the field as invalid
11068 * if the validation fails
11069 * @param {Mixed} value The value to validate
11070 * @return {Boolean} True if the value is valid, else false
11072 validateValue : function(value)
11074 if(this.getVisibilityEl().hasClass('hidden')){
11078 if(value.length < 1) { // if it's blank
11079 if(this.allowBlank){
11085 if(value.length < this.minLength){
11088 if(value.length > this.maxLength){
11092 var vt = Roo.form.VTypes;
11093 if(!vt[this.vtype](value, this)){
11097 if(typeof this.validator == "function"){
11098 var msg = this.validator(value);
11102 if (typeof(msg) == 'string') {
11103 this.invalidText = msg;
11107 if(this.regex && !this.regex.test(value)){
11115 fireKey : function(e){
11116 //Roo.log('field ' + e.getKey());
11117 if(e.isNavKeyPress()){
11118 this.fireEvent("specialkey", this, e);
11121 focus : function (selectText){
11123 this.inputEl().focus();
11124 if(selectText === true){
11125 this.inputEl().dom.select();
11131 onFocus : function(){
11132 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133 // this.el.addClass(this.focusClass);
11135 if(!this.hasFocus){
11136 this.hasFocus = true;
11137 this.startValue = this.getValue();
11138 this.fireEvent("focus", this);
11142 beforeBlur : Roo.emptyFn,
11146 onBlur : function(){
11148 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149 //this.el.removeClass(this.focusClass);
11151 this.hasFocus = false;
11152 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11155 var v = this.getValue();
11156 if(String(v) !== String(this.startValue)){
11157 this.fireEvent('change', this, v, this.startValue);
11159 this.fireEvent("blur", this);
11162 onChange : function(e)
11164 var v = this.getValue();
11165 if(String(v) !== String(this.startValue)){
11166 this.fireEvent('change', this, v, this.startValue);
11172 * Resets the current field value to the originally loaded value and clears any validation messages
11174 reset : function(){
11175 this.setValue(this.originalValue);
11179 * Returns the name of the field
11180 * @return {Mixed} name The name field
11182 getName: function(){
11186 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11187 * @return {Mixed} value The field value
11189 getValue : function(){
11191 var v = this.inputEl().getValue();
11196 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11197 * @return {Mixed} value The field value
11199 getRawValue : function(){
11200 var v = this.inputEl().getValue();
11206 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11207 * @param {Mixed} value The value to set
11209 setRawValue : function(v){
11210 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11213 selectText : function(start, end){
11214 var v = this.getRawValue();
11216 start = start === undefined ? 0 : start;
11217 end = end === undefined ? v.length : end;
11218 var d = this.inputEl().dom;
11219 if(d.setSelectionRange){
11220 d.setSelectionRange(start, end);
11221 }else if(d.createTextRange){
11222 var range = d.createTextRange();
11223 range.moveStart("character", start);
11224 range.moveEnd("character", v.length-end);
11231 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11232 * @param {Mixed} value The value to set
11234 setValue : function(v){
11237 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11243 processValue : function(value){
11244 if(this.stripCharsRe){
11245 var newValue = value.replace(this.stripCharsRe, '');
11246 if(newValue !== value){
11247 this.setRawValue(newValue);
11254 preFocus : function(){
11256 if(this.selectOnFocus){
11257 this.inputEl().dom.select();
11260 filterKeys : function(e){
11261 var k = e.getKey();
11262 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11265 var c = e.getCharCode(), cc = String.fromCharCode(c);
11266 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11269 if(!this.maskRe.test(cc)){
11274 * Clear any invalid styles/messages for this field
11276 clearInvalid : function(){
11278 if(!this.el || this.preventMark){ // not rendered
11283 this.el.removeClass([this.invalidClass, 'is-invalid']);
11285 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11287 var feedback = this.el.select('.form-control-feedback', true).first();
11290 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11295 if(this.indicator){
11296 this.indicator.removeClass('visible');
11297 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11300 this.fireEvent('valid', this);
11304 * Mark this field as valid
11306 markValid : function()
11308 if(!this.el || this.preventMark){ // not rendered...
11312 this.el.removeClass([this.invalidClass, this.validClass]);
11313 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11315 var feedback = this.el.select('.form-control-feedback', true).first();
11318 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11321 if(this.indicator){
11322 this.indicator.removeClass('visible');
11323 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11331 if(this.allowBlank && !this.getRawValue().length){
11334 if (Roo.bootstrap.version == 3) {
11335 this.el.addClass(this.validClass);
11337 this.inputEl().addClass('is-valid');
11340 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11342 var feedback = this.el.select('.form-control-feedback', true).first();
11345 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11351 this.fireEvent('valid', this);
11355 * Mark this field as invalid
11356 * @param {String} msg The validation message
11358 markInvalid : function(msg)
11360 if(!this.el || this.preventMark){ // not rendered
11364 this.el.removeClass([this.invalidClass, this.validClass]);
11365 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11367 var feedback = this.el.select('.form-control-feedback', true).first();
11370 this.el.select('.form-control-feedback', true).first().removeClass(
11371 [this.invalidFeedbackClass, this.validFeedbackClass]);
11378 if(this.allowBlank && !this.getRawValue().length){
11382 if(this.indicator){
11383 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384 this.indicator.addClass('visible');
11386 if (Roo.bootstrap.version == 3) {
11387 this.el.addClass(this.invalidClass);
11389 this.inputEl().addClass('is-invalid');
11394 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11396 var feedback = this.el.select('.form-control-feedback', true).first();
11399 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11401 if(this.getValue().length || this.forceFeedback){
11402 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11409 this.fireEvent('invalid', this, msg);
11412 SafariOnKeyDown : function(event)
11414 // this is a workaround for a password hang bug on chrome/ webkit.
11415 if (this.inputEl().dom.type != 'password') {
11419 var isSelectAll = false;
11421 if(this.inputEl().dom.selectionEnd > 0){
11422 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11424 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425 event.preventDefault();
11430 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11432 event.preventDefault();
11433 // this is very hacky as keydown always get's upper case.
11435 var cc = String.fromCharCode(event.getCharCode());
11436 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11440 adjustWidth : function(tag, w){
11441 tag = tag.toLowerCase();
11442 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444 if(tag == 'input'){
11447 if(tag == 'textarea'){
11450 }else if(Roo.isOpera){
11451 if(tag == 'input'){
11454 if(tag == 'textarea'){
11462 setFieldLabel : function(v)
11464 if(!this.rendered){
11468 if(this.indicatorEl()){
11469 var ar = this.el.select('label > span',true);
11471 if (ar.elements.length) {
11472 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473 this.fieldLabel = v;
11477 var br = this.el.select('label',true);
11479 if(br.elements.length) {
11480 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481 this.fieldLabel = v;
11485 Roo.log('Cannot Found any of label > span || label in input');
11489 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490 this.fieldLabel = v;
11505 * @class Roo.bootstrap.TextArea
11506 * @extends Roo.bootstrap.Input
11507 * Bootstrap TextArea class
11508 * @cfg {Number} cols Specifies the visible width of a text area
11509 * @cfg {Number} rows Specifies the visible number of lines in a text area
11510 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512 * @cfg {string} html text
11515 * Create a new TextArea
11516 * @param {Object} config The config object
11519 Roo.bootstrap.TextArea = function(config){
11520 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11534 getAutoCreate : function(){
11536 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11542 if(this.inputType != 'hidden'){
11543 cfg.cls = 'form-group' //input-group
11551 value : this.value || '',
11552 html: this.html || '',
11553 cls : 'form-control',
11554 placeholder : this.placeholder || ''
11558 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559 input.maxLength = this.maxLength;
11563 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11567 input.cols = this.cols;
11570 if (this.readOnly) {
11571 input.readonly = true;
11575 input.name = this.name;
11579 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11583 ['xs','sm','md','lg'].map(function(size){
11584 if (settings[size]) {
11585 cfg.cls += ' col-' + size + '-' + settings[size];
11589 var inputblock = input;
11591 if(this.hasFeedback && !this.allowBlank){
11595 cls: 'glyphicon form-control-feedback'
11599 cls : 'has-feedback',
11608 if (this.before || this.after) {
11611 cls : 'input-group',
11615 inputblock.cn.push({
11617 cls : 'input-group-addon',
11622 inputblock.cn.push(input);
11624 if(this.hasFeedback && !this.allowBlank){
11625 inputblock.cls += ' has-feedback';
11626 inputblock.cn.push(feedback);
11630 inputblock.cn.push({
11632 cls : 'input-group-addon',
11639 if (align ==='left' && this.fieldLabel.length) {
11644 cls : 'control-label',
11645 html : this.fieldLabel
11656 if(this.labelWidth > 12){
11657 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11660 if(this.labelWidth < 13 && this.labelmd == 0){
11661 this.labelmd = this.labelWidth;
11664 if(this.labellg > 0){
11665 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11669 if(this.labelmd > 0){
11670 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11674 if(this.labelsm > 0){
11675 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11679 if(this.labelxs > 0){
11680 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11684 } else if ( this.fieldLabel.length) {
11689 //cls : 'input-group-addon',
11690 html : this.fieldLabel
11708 if (this.disabled) {
11709 input.disabled=true;
11716 * return the real textarea element.
11718 inputEl: function ()
11720 return this.el.select('textarea.form-control',true).first();
11724 * Clear any invalid styles/messages for this field
11726 clearInvalid : function()
11729 if(!this.el || this.preventMark){ // not rendered
11733 var label = this.el.select('label', true).first();
11734 var icon = this.el.select('i.fa-star', true).first();
11739 this.el.removeClass( this.validClass);
11740 this.inputEl().removeClass('is-invalid');
11742 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11744 var feedback = this.el.select('.form-control-feedback', true).first();
11747 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11752 this.fireEvent('valid', this);
11756 * Mark this field as valid
11758 markValid : function()
11760 if(!this.el || this.preventMark){ // not rendered
11764 this.el.removeClass([this.invalidClass, this.validClass]);
11765 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11767 var feedback = this.el.select('.form-control-feedback', true).first();
11770 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11773 if(this.disabled || this.allowBlank){
11777 var label = this.el.select('label', true).first();
11778 var icon = this.el.select('i.fa-star', true).first();
11783 if (Roo.bootstrap.version == 3) {
11784 this.el.addClass(this.validClass);
11786 this.inputEl().addClass('is-valid');
11790 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11792 var feedback = this.el.select('.form-control-feedback', true).first();
11795 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11801 this.fireEvent('valid', this);
11805 * Mark this field as invalid
11806 * @param {String} msg The validation message
11808 markInvalid : function(msg)
11810 if(!this.el || this.preventMark){ // not rendered
11814 this.el.removeClass([this.invalidClass, this.validClass]);
11815 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11817 var feedback = this.el.select('.form-control-feedback', true).first();
11820 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11823 if(this.disabled || this.allowBlank){
11827 var label = this.el.select('label', true).first();
11828 var icon = this.el.select('i.fa-star', true).first();
11830 if(!this.getValue().length && label && !icon){
11831 this.el.createChild({
11833 cls : 'text-danger fa fa-lg fa-star',
11834 tooltip : 'This field is required',
11835 style : 'margin-right:5px;'
11839 if (Roo.bootstrap.version == 3) {
11840 this.el.addClass(this.invalidClass);
11842 this.inputEl().addClass('is-invalid');
11845 // fixme ... this may be depricated need to test..
11846 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11848 var feedback = this.el.select('.form-control-feedback', true).first();
11851 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11853 if(this.getValue().length || this.forceFeedback){
11854 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11861 this.fireEvent('invalid', this, msg);
11869 * trigger field - base class for combo..
11874 * @class Roo.bootstrap.TriggerField
11875 * @extends Roo.bootstrap.Input
11876 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879 * for which you can provide a custom implementation. For example:
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11886 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11889 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11893 * Create a new TriggerField.
11894 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895 * to the base TextField)
11897 Roo.bootstrap.TriggerField = function(config){
11898 this.mimicing = false;
11899 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11904 * @cfg {String} triggerClass A CSS class to apply to the trigger
11907 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11912 * @cfg {Boolean} removable (true|false) special filter default false
11916 /** @cfg {Boolean} grow @hide */
11917 /** @cfg {Number} growMin @hide */
11918 /** @cfg {Number} growMax @hide */
11924 autoSize: Roo.emptyFn,
11928 deferHeight : true,
11931 actionMode : 'wrap',
11936 getAutoCreate : function(){
11938 var align = this.labelAlign || this.parentLabelAlign();
11943 cls: 'form-group' //input-group
11950 type : this.inputType,
11951 cls : 'form-control',
11952 autocomplete: 'new-password',
11953 placeholder : this.placeholder || ''
11957 input.name = this.name;
11960 input.cls += ' input-' + this.size;
11963 if (this.disabled) {
11964 input.disabled=true;
11967 var inputblock = input;
11969 if(this.hasFeedback && !this.allowBlank){
11973 cls: 'glyphicon form-control-feedback'
11976 if(this.removable && !this.editable ){
11978 cls : 'has-feedback',
11984 cls : 'roo-combo-removable-btn close'
11991 cls : 'has-feedback',
12000 if(this.removable && !this.editable ){
12002 cls : 'roo-removable',
12008 cls : 'roo-combo-removable-btn close'
12015 if (this.before || this.after) {
12018 cls : 'input-group',
12022 inputblock.cn.push({
12024 cls : 'input-group-addon input-group-prepend input-group-text',
12029 inputblock.cn.push(input);
12031 if(this.hasFeedback && !this.allowBlank){
12032 inputblock.cls += ' has-feedback';
12033 inputblock.cn.push(feedback);
12037 inputblock.cn.push({
12039 cls : 'input-group-addon input-group-append input-group-text',
12048 var ibwrap = inputblock;
12053 cls: 'roo-select2-choices',
12057 cls: 'roo-select2-search-field',
12069 cls: 'roo-select2-container input-group',
12074 cls: 'form-hidden-field'
12080 if(!this.multiple && this.showToggleBtn){
12086 if (this.caret != false) {
12089 cls: 'fa fa-' + this.caret
12096 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12098 Roo.bootstrap.version == 3 ? caret : '',
12101 cls: 'combobox-clear',
12115 combobox.cls += ' roo-select2-container-multi';
12119 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120 tooltip : 'This field is required'
12122 if (Roo.bootstrap.version == 4) {
12125 style : 'display:none'
12130 if (align ==='left' && this.fieldLabel.length) {
12132 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12139 cls : 'control-label',
12140 html : this.fieldLabel
12152 var labelCfg = cfg.cn[1];
12153 var contentCfg = cfg.cn[2];
12155 if(this.indicatorpos == 'right'){
12160 cls : 'control-label',
12164 html : this.fieldLabel
12178 labelCfg = cfg.cn[0];
12179 contentCfg = cfg.cn[1];
12182 if(this.labelWidth > 12){
12183 labelCfg.style = "width: " + this.labelWidth + 'px';
12186 if(this.labelWidth < 13 && this.labelmd == 0){
12187 this.labelmd = this.labelWidth;
12190 if(this.labellg > 0){
12191 labelCfg.cls += ' col-lg-' + this.labellg;
12192 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12195 if(this.labelmd > 0){
12196 labelCfg.cls += ' col-md-' + this.labelmd;
12197 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12200 if(this.labelsm > 0){
12201 labelCfg.cls += ' col-sm-' + this.labelsm;
12202 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12205 if(this.labelxs > 0){
12206 labelCfg.cls += ' col-xs-' + this.labelxs;
12207 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12210 } else if ( this.fieldLabel.length) {
12211 // Roo.log(" label");
12216 //cls : 'input-group-addon',
12217 html : this.fieldLabel
12225 if(this.indicatorpos == 'right'){
12233 html : this.fieldLabel
12247 // Roo.log(" no label && no align");
12254 ['xs','sm','md','lg'].map(function(size){
12255 if (settings[size]) {
12256 cfg.cls += ' col-' + size + '-' + settings[size];
12267 onResize : function(w, h){
12268 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 // if(typeof w == 'number'){
12270 // var x = w - this.trigger.getWidth();
12271 // this.inputEl().setWidth(this.adjustWidth('input', x));
12272 // this.trigger.setStyle('left', x+'px');
12277 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12280 getResizeEl : function(){
12281 return this.inputEl();
12285 getPositionEl : function(){
12286 return this.inputEl();
12290 alignErrorIcon : function(){
12291 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12295 initEvents : function(){
12299 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301 if(!this.multiple && this.showToggleBtn){
12302 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303 if(this.hideTrigger){
12304 this.trigger.setDisplayed(false);
12306 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12310 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12313 if(this.removable && !this.editable && !this.tickable){
12314 var close = this.closeTriggerEl();
12317 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318 close.on('click', this.removeBtnClick, this, close);
12322 //this.trigger.addClassOnOver('x-form-trigger-over');
12323 //this.trigger.addClassOnClick('x-form-trigger-click');
12326 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12330 closeTriggerEl : function()
12332 var close = this.el.select('.roo-combo-removable-btn', true).first();
12333 return close ? close : false;
12336 removeBtnClick : function(e, h, el)
12338 e.preventDefault();
12340 if(this.fireEvent("remove", this) !== false){
12342 this.fireEvent("afterremove", this)
12346 createList : function()
12348 this.list = Roo.get(document.body).createChild({
12349 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350 cls: 'typeahead typeahead-long dropdown-menu shadow',
12351 style: 'display:none'
12354 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12359 initTrigger : function(){
12364 onDestroy : function(){
12366 this.trigger.removeAllListeners();
12367 // this.trigger.remove();
12370 // this.wrap.remove();
12372 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12376 onFocus : function(){
12377 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12379 if(!this.mimicing){
12380 this.wrap.addClass('x-trigger-wrap-focus');
12381 this.mimicing = true;
12382 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383 if(this.monitorTab){
12384 this.el.on("keydown", this.checkTab, this);
12391 checkTab : function(e){
12392 if(e.getKey() == e.TAB){
12393 this.triggerBlur();
12398 onBlur : function(){
12403 mimicBlur : function(e, t){
12405 if(!this.wrap.contains(t) && this.validateBlur()){
12406 this.triggerBlur();
12412 triggerBlur : function(){
12413 this.mimicing = false;
12414 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415 if(this.monitorTab){
12416 this.el.un("keydown", this.checkTab, this);
12418 //this.wrap.removeClass('x-trigger-wrap-focus');
12419 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12423 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424 validateBlur : function(e, t){
12429 onDisable : function(){
12430 this.inputEl().dom.disabled = true;
12431 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12433 // this.wrap.addClass('x-item-disabled');
12438 onEnable : function(){
12439 this.inputEl().dom.disabled = false;
12440 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12442 // this.el.removeClass('x-item-disabled');
12447 onShow : function(){
12448 var ae = this.getActionEl();
12451 ae.dom.style.display = '';
12452 ae.dom.style.visibility = 'visible';
12458 onHide : function(){
12459 var ae = this.getActionEl();
12460 ae.dom.style.display = 'none';
12464 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12465 * by an implementing function.
12467 * @param {EventObject} e
12469 onTriggerClick : Roo.emptyFn
12477 * @class Roo.bootstrap.CardUploader
12478 * @extends Roo.bootstrap.Button
12479 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480 * @cfg {Number} errorTimeout default 3000
12481 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12482 * @cfg {Array} html The button text.
12486 * Create a new CardUploader
12487 * @param {Object} config The config object
12490 Roo.bootstrap.CardUploader = function(config){
12494 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12497 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12507 errorTimeout : 3000,
12511 fileCollection : false,
12514 getAutoCreate : function()
12518 cls :'form-group' ,
12523 //cls : 'input-group-addon',
12524 html : this.fieldLabel
12532 value : this.value,
12533 cls : 'd-none form-control'
12538 multiple : 'multiple',
12540 cls : 'd-none roo-card-upload-selector'
12544 cls : 'roo-card-uploader-button-container w-100 mb-2'
12547 cls : 'card-columns roo-card-uploader-container'
12557 getChildContainer : function() /// what children are added to.
12559 return this.containerEl;
12562 getButtonContainer : function() /// what children are added to.
12564 return this.el.select(".roo-card-uploader-button-container").first();
12567 initEvents : function()
12570 Roo.bootstrap.Input.prototype.initEvents.call(this);
12574 xns: Roo.bootstrap,
12577 container_method : 'getButtonContainer' ,
12578 html : this.html, // fix changable?
12581 'click' : function(btn, e) {
12590 this.urlAPI = (window.createObjectURL && window) ||
12591 (window.URL && URL.revokeObjectURL && URL) ||
12592 (window.webkitURL && webkitURL);
12597 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12599 this.selectorEl.on('change', this.onFileSelected, this);
12602 this.images.forEach(function(img) {
12605 this.images = false;
12607 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12613 onClick : function(e)
12615 e.preventDefault();
12617 this.selectorEl.dom.click();
12621 onFileSelected : function(e)
12623 e.preventDefault();
12625 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12629 Roo.each(this.selectorEl.dom.files, function(file){
12630 this.addFile(file);
12639 addFile : function(file)
12642 if(typeof(file) === 'string'){
12643 throw "Add file by name?"; // should not happen
12647 if(!file || !this.urlAPI){
12657 var url = _this.urlAPI.createObjectURL( file);
12660 id : Roo.bootstrap.CardUploader.ID--,
12661 is_uploaded : false,
12664 mimetype : file.type,
12671 addCard : function (data)
12673 // hidden input element?
12674 // if the file is not an image...
12675 //then we need to use something other that and header_image
12680 xns : Roo.bootstrap,
12681 xtype : 'CardFooter',
12684 xns : Roo.bootstrap,
12690 xns : Roo.bootstrap,
12692 html : String.format("<small>{0}</small>", data.title),
12693 cls : 'col-11 text-left',
12698 click : function() {
12699 this.downloadCard(data.id)
12705 xns : Roo.bootstrap,
12713 click : function() {
12714 t.removeCard(data.id)
12726 var cn = this.addxtype(
12729 xns : Roo.bootstrap,
12732 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12733 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12734 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12739 initEvents : function() {
12740 Roo.bootstrap.Card.prototype.initEvents.call(this);
12741 this.imgEl = this.el.select('.card-img-top').first();
12743 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12744 this.imgEl.set({ 'pointer' : 'cursor' });
12753 // dont' really need ot update items.
12754 // this.items.push(cn);
12755 this.fileCollection.add(cn);
12758 var reader = new FileReader();
12759 reader.onloadend = function(evt) {
12760 data.srcdata = evt.target.result;
12763 reader.readAsDataURL(data.src);
12768 removeCard : function(id)
12771 var card = this.fileCollection.get(id);
12772 card.data.is_deleted = 1;
12773 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12774 this.fileCollection.remove(card);
12775 //this.items = this.items.filter(function(e) { return e != card });
12776 // dont' really need ot update items.
12777 card.el.dom.parentNode.removeChild(card.el.dom);
12782 this.fileCollection.each(function(card) {
12783 card.el.dom.parentNode.removeChild(card.el.dom);
12785 this.fileCollection.clear();
12786 this.updateInput();
12789 updateInput : function()
12793 var dom = this.inputEl().dom;
12794 var fc = this.fileCollection;
12795 var next = function() {
12796 if (i >= fc.length) {
12797 dom.value = JSON.stringify(data);
12800 var reader = new FileReader();
12801 reader.onloadend = function(evt) {
12803 var ee = Roo.apply({}, fc[i]);
12804 ee.src = evt.target.result;
12809 reader.readAsDataURL(fc[i].src);
12821 Roo.bootstrap.CardUploader.ID = -1;/*
12823 * Ext JS Library 1.1.1
12824 * Copyright(c) 2006-2007, Ext JS, LLC.
12826 * Originally Released Under LGPL - original licence link has changed is not relivant.
12829 * <script type="text/javascript">
12834 * @class Roo.data.SortTypes
12836 * Defines the default sorting (casting?) comparison functions used when sorting data.
12838 Roo.data.SortTypes = {
12840 * Default sort that does nothing
12841 * @param {Mixed} s The value being converted
12842 * @return {Mixed} The comparison value
12844 none : function(s){
12849 * The regular expression used to strip tags
12853 stripTagsRE : /<\/?[^>]+>/gi,
12856 * Strips all HTML tags to sort on text only
12857 * @param {Mixed} s The value being converted
12858 * @return {String} The comparison value
12860 asText : function(s){
12861 return String(s).replace(this.stripTagsRE, "");
12865 * Strips all HTML tags to sort on text only - Case insensitive
12866 * @param {Mixed} s The value being converted
12867 * @return {String} The comparison value
12869 asUCText : function(s){
12870 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12874 * Case insensitive string
12875 * @param {Mixed} s The value being converted
12876 * @return {String} The comparison value
12878 asUCString : function(s) {
12879 return String(s).toUpperCase();
12884 * @param {Mixed} s The value being converted
12885 * @return {Number} The comparison value
12887 asDate : function(s) {
12891 if(s instanceof Date){
12892 return s.getTime();
12894 return Date.parse(String(s));
12899 * @param {Mixed} s The value being converted
12900 * @return {Float} The comparison value
12902 asFloat : function(s) {
12903 var val = parseFloat(String(s).replace(/,/g, ""));
12912 * @param {Mixed} s The value being converted
12913 * @return {Number} The comparison value
12915 asInt : function(s) {
12916 var val = parseInt(String(s).replace(/,/g, ""));
12924 * Ext JS Library 1.1.1
12925 * Copyright(c) 2006-2007, Ext JS, LLC.
12927 * Originally Released Under LGPL - original licence link has changed is not relivant.
12930 * <script type="text/javascript">
12934 * @class Roo.data.Record
12935 * Instances of this class encapsulate both record <em>definition</em> information, and record
12936 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12937 * to access Records cached in an {@link Roo.data.Store} object.<br>
12939 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12940 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12943 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12945 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12946 * {@link #create}. The parameters are the same.
12947 * @param {Array} data An associative Array of data values keyed by the field name.
12948 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12949 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12950 * not specified an integer id is generated.
12952 Roo.data.Record = function(data, id){
12953 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12958 * Generate a constructor for a specific record layout.
12959 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12960 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12961 * Each field definition object may contain the following properties: <ul>
12962 * <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,
12963 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12964 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12965 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12966 * is being used, then this is a string containing the javascript expression to reference the data relative to
12967 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12968 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12969 * this may be omitted.</p></li>
12970 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12971 * <ul><li>auto (Default, implies no conversion)</li>
12976 * <li>date</li></ul></p></li>
12977 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12978 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12979 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12980 * by the Reader into an object that will be stored in the Record. It is passed the
12981 * following parameters:<ul>
12982 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12984 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12986 * <br>usage:<br><pre><code>
12987 var TopicRecord = Roo.data.Record.create(
12988 {name: 'title', mapping: 'topic_title'},
12989 {name: 'author', mapping: 'username'},
12990 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12991 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12992 {name: 'lastPoster', mapping: 'user2'},
12993 {name: 'excerpt', mapping: 'post_text'}
12996 var myNewRecord = new TopicRecord({
12997 title: 'Do my job please',
13000 lastPost: new Date(),
13001 lastPoster: 'Animal',
13002 excerpt: 'No way dude!'
13004 myStore.add(myNewRecord);
13009 Roo.data.Record.create = function(o){
13010 var f = function(){
13011 f.superclass.constructor.apply(this, arguments);
13013 Roo.extend(f, Roo.data.Record);
13014 var p = f.prototype;
13015 p.fields = new Roo.util.MixedCollection(false, function(field){
13018 for(var i = 0, len = o.length; i < len; i++){
13019 p.fields.add(new Roo.data.Field(o[i]));
13021 f.getField = function(name){
13022 return p.fields.get(name);
13027 Roo.data.Record.AUTO_ID = 1000;
13028 Roo.data.Record.EDIT = 'edit';
13029 Roo.data.Record.REJECT = 'reject';
13030 Roo.data.Record.COMMIT = 'commit';
13032 Roo.data.Record.prototype = {
13034 * Readonly flag - true if this record has been modified.
13043 join : function(store){
13044 this.store = store;
13048 * Set the named field to the specified value.
13049 * @param {String} name The name of the field to set.
13050 * @param {Object} value The value to set the field to.
13052 set : function(name, value){
13053 if(this.data[name] == value){
13057 if(!this.modified){
13058 this.modified = {};
13060 if(typeof this.modified[name] == 'undefined'){
13061 this.modified[name] = this.data[name];
13063 this.data[name] = value;
13064 if(!this.editing && this.store){
13065 this.store.afterEdit(this);
13070 * Get the value of the named field.
13071 * @param {String} name The name of the field to get the value of.
13072 * @return {Object} The value of the field.
13074 get : function(name){
13075 return this.data[name];
13079 beginEdit : function(){
13080 this.editing = true;
13081 this.modified = {};
13085 cancelEdit : function(){
13086 this.editing = false;
13087 delete this.modified;
13091 endEdit : function(){
13092 this.editing = false;
13093 if(this.dirty && this.store){
13094 this.store.afterEdit(this);
13099 * Usually called by the {@link Roo.data.Store} which owns the Record.
13100 * Rejects all changes made to the Record since either creation, or the last commit operation.
13101 * Modified fields are reverted to their original values.
13103 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13104 * of reject operations.
13106 reject : function(){
13107 var m = this.modified;
13109 if(typeof m[n] != "function"){
13110 this.data[n] = m[n];
13113 this.dirty = false;
13114 delete this.modified;
13115 this.editing = false;
13117 this.store.afterReject(this);
13122 * Usually called by the {@link Roo.data.Store} which owns the Record.
13123 * Commits all changes made to the Record since either creation, or the last commit operation.
13125 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13126 * of commit operations.
13128 commit : function(){
13129 this.dirty = false;
13130 delete this.modified;
13131 this.editing = false;
13133 this.store.afterCommit(this);
13138 hasError : function(){
13139 return this.error != null;
13143 clearError : function(){
13148 * Creates a copy of this record.
13149 * @param {String} id (optional) A new record id if you don't want to use this record's id
13152 copy : function(newId) {
13153 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13157 * Ext JS Library 1.1.1
13158 * Copyright(c) 2006-2007, Ext JS, LLC.
13160 * Originally Released Under LGPL - original licence link has changed is not relivant.
13163 * <script type="text/javascript">
13169 * @class Roo.data.Store
13170 * @extends Roo.util.Observable
13171 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13172 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13174 * 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
13175 * has no knowledge of the format of the data returned by the Proxy.<br>
13177 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13178 * instances from the data object. These records are cached and made available through accessor functions.
13180 * Creates a new Store.
13181 * @param {Object} config A config object containing the objects needed for the Store to access data,
13182 * and read the data into Records.
13184 Roo.data.Store = function(config){
13185 this.data = new Roo.util.MixedCollection(false);
13186 this.data.getKey = function(o){
13189 this.baseParams = {};
13191 this.paramNames = {
13196 "multisort" : "_multisort"
13199 if(config && config.data){
13200 this.inlineData = config.data;
13201 delete config.data;
13204 Roo.apply(this, config);
13206 if(this.reader){ // reader passed
13207 this.reader = Roo.factory(this.reader, Roo.data);
13208 this.reader.xmodule = this.xmodule || false;
13209 if(!this.recordType){
13210 this.recordType = this.reader.recordType;
13212 if(this.reader.onMetaChange){
13213 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13217 if(this.recordType){
13218 this.fields = this.recordType.prototype.fields;
13220 this.modified = [];
13224 * @event datachanged
13225 * Fires when the data cache has changed, and a widget which is using this Store
13226 * as a Record cache should refresh its view.
13227 * @param {Store} this
13229 datachanged : true,
13231 * @event metachange
13232 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13233 * @param {Store} this
13234 * @param {Object} meta The JSON metadata
13239 * Fires when Records have been added to the Store
13240 * @param {Store} this
13241 * @param {Roo.data.Record[]} records The array of Records added
13242 * @param {Number} index The index at which the record(s) were added
13247 * Fires when a Record has been removed from the Store
13248 * @param {Store} this
13249 * @param {Roo.data.Record} record The Record that was removed
13250 * @param {Number} index The index at which the record was removed
13255 * Fires when a Record has been updated
13256 * @param {Store} this
13257 * @param {Roo.data.Record} record The Record that was updated
13258 * @param {String} operation The update operation being performed. Value may be one of:
13260 Roo.data.Record.EDIT
13261 Roo.data.Record.REJECT
13262 Roo.data.Record.COMMIT
13268 * Fires when the data cache has been cleared.
13269 * @param {Store} this
13273 * @event beforeload
13274 * Fires before a request is made for a new data object. If the beforeload handler returns false
13275 * the load action will be canceled.
13276 * @param {Store} this
13277 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13281 * @event beforeloadadd
13282 * Fires after a new set of Records has been loaded.
13283 * @param {Store} this
13284 * @param {Roo.data.Record[]} records The Records that were loaded
13285 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13287 beforeloadadd : true,
13290 * Fires after a new set of Records has been loaded, before they are added to the store.
13291 * @param {Store} this
13292 * @param {Roo.data.Record[]} records The Records that were loaded
13293 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13294 * @params {Object} return from reader
13298 * @event loadexception
13299 * Fires if an exception occurs in the Proxy during loading.
13300 * Called with the signature of the Proxy's "loadexception" event.
13301 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13304 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13305 * @param {Object} load options
13306 * @param {Object} jsonData from your request (normally this contains the Exception)
13308 loadexception : true
13312 this.proxy = Roo.factory(this.proxy, Roo.data);
13313 this.proxy.xmodule = this.xmodule || false;
13314 this.relayEvents(this.proxy, ["loadexception"]);
13316 this.sortToggle = {};
13317 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13319 Roo.data.Store.superclass.constructor.call(this);
13321 if(this.inlineData){
13322 this.loadData(this.inlineData);
13323 delete this.inlineData;
13327 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13329 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13330 * without a remote query - used by combo/forms at present.
13334 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13337 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13340 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13341 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13344 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13345 * on any HTTP request
13348 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13351 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13355 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13356 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13358 remoteSort : false,
13361 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13362 * loaded or when a record is removed. (defaults to false).
13364 pruneModifiedRecords : false,
13367 lastOptions : null,
13370 * Add Records to the Store and fires the add event.
13371 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13373 add : function(records){
13374 records = [].concat(records);
13375 for(var i = 0, len = records.length; i < len; i++){
13376 records[i].join(this);
13378 var index = this.data.length;
13379 this.data.addAll(records);
13380 this.fireEvent("add", this, records, index);
13384 * Remove a Record from the Store and fires the remove event.
13385 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13387 remove : function(record){
13388 var index = this.data.indexOf(record);
13389 this.data.removeAt(index);
13391 if(this.pruneModifiedRecords){
13392 this.modified.remove(record);
13394 this.fireEvent("remove", this, record, index);
13398 * Remove all Records from the Store and fires the clear event.
13400 removeAll : function(){
13402 if(this.pruneModifiedRecords){
13403 this.modified = [];
13405 this.fireEvent("clear", this);
13409 * Inserts Records to the Store at the given index and fires the add event.
13410 * @param {Number} index The start index at which to insert the passed Records.
13411 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13413 insert : function(index, records){
13414 records = [].concat(records);
13415 for(var i = 0, len = records.length; i < len; i++){
13416 this.data.insert(index, records[i]);
13417 records[i].join(this);
13419 this.fireEvent("add", this, records, index);
13423 * Get the index within the cache of the passed Record.
13424 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13425 * @return {Number} The index of the passed Record. Returns -1 if not found.
13427 indexOf : function(record){
13428 return this.data.indexOf(record);
13432 * Get the index within the cache of the Record with the passed id.
13433 * @param {String} id The id of the Record to find.
13434 * @return {Number} The index of the Record. Returns -1 if not found.
13436 indexOfId : function(id){
13437 return this.data.indexOfKey(id);
13441 * Get the Record with the specified id.
13442 * @param {String} id The id of the Record to find.
13443 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13445 getById : function(id){
13446 return this.data.key(id);
13450 * Get the Record at the specified index.
13451 * @param {Number} index The index of the Record to find.
13452 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13454 getAt : function(index){
13455 return this.data.itemAt(index);
13459 * Returns a range of Records between specified indices.
13460 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13461 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13462 * @return {Roo.data.Record[]} An array of Records
13464 getRange : function(start, end){
13465 return this.data.getRange(start, end);
13469 storeOptions : function(o){
13470 o = Roo.apply({}, o);
13473 this.lastOptions = o;
13477 * Loads the Record cache from the configured Proxy using the configured Reader.
13479 * If using remote paging, then the first load call must specify the <em>start</em>
13480 * and <em>limit</em> properties in the options.params property to establish the initial
13481 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13483 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13484 * and this call will return before the new data has been loaded. Perform any post-processing
13485 * in a callback function, or in a "load" event handler.</strong>
13487 * @param {Object} options An object containing properties which control loading options:<ul>
13488 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13489 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13490 * passed the following arguments:<ul>
13491 * <li>r : Roo.data.Record[]</li>
13492 * <li>options: Options object from the load call</li>
13493 * <li>success: Boolean success indicator</li></ul></li>
13494 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13495 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13498 load : function(options){
13499 options = options || {};
13500 if(this.fireEvent("beforeload", this, options) !== false){
13501 this.storeOptions(options);
13502 var p = Roo.apply(options.params || {}, this.baseParams);
13503 // if meta was not loaded from remote source.. try requesting it.
13504 if (!this.reader.metaFromRemote) {
13505 p._requestMeta = 1;
13507 if(this.sortInfo && this.remoteSort){
13508 var pn = this.paramNames;
13509 p[pn["sort"]] = this.sortInfo.field;
13510 p[pn["dir"]] = this.sortInfo.direction;
13512 if (this.multiSort) {
13513 var pn = this.paramNames;
13514 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13517 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13522 * Reloads the Record cache from the configured Proxy using the configured Reader and
13523 * the options from the last load operation performed.
13524 * @param {Object} options (optional) An object containing properties which may override the options
13525 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13526 * the most recently used options are reused).
13528 reload : function(options){
13529 this.load(Roo.applyIf(options||{}, this.lastOptions));
13533 // Called as a callback by the Reader during a load operation.
13534 loadRecords : function(o, options, success){
13535 if(!o || success === false){
13536 if(success !== false){
13537 this.fireEvent("load", this, [], options, o);
13539 if(options.callback){
13540 options.callback.call(options.scope || this, [], options, false);
13544 // if data returned failure - throw an exception.
13545 if (o.success === false) {
13546 // show a message if no listener is registered.
13547 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13548 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13550 // loadmask wil be hooked into this..
13551 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13554 var r = o.records, t = o.totalRecords || r.length;
13556 this.fireEvent("beforeloadadd", this, r, options, o);
13558 if(!options || options.add !== true){
13559 if(this.pruneModifiedRecords){
13560 this.modified = [];
13562 for(var i = 0, len = r.length; i < len; i++){
13566 this.data = this.snapshot;
13567 delete this.snapshot;
13570 this.data.addAll(r);
13571 this.totalLength = t;
13573 this.fireEvent("datachanged", this);
13575 this.totalLength = Math.max(t, this.data.length+r.length);
13579 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13581 var e = new Roo.data.Record({});
13583 e.set(this.parent.displayField, this.parent.emptyTitle);
13584 e.set(this.parent.valueField, '');
13589 this.fireEvent("load", this, r, options, o);
13590 if(options.callback){
13591 options.callback.call(options.scope || this, r, options, true);
13597 * Loads data from a passed data block. A Reader which understands the format of the data
13598 * must have been configured in the constructor.
13599 * @param {Object} data The data block from which to read the Records. The format of the data expected
13600 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13601 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13603 loadData : function(o, append){
13604 var r = this.reader.readRecords(o);
13605 this.loadRecords(r, {add: append}, true);
13609 * using 'cn' the nested child reader read the child array into it's child stores.
13610 * @param {Object} rec The record with a 'children array
13612 loadDataFromChildren : function(rec)
13614 this.loadData(this.reader.toLoadData(rec));
13619 * Gets the number of cached records.
13621 * <em>If using paging, this may not be the total size of the dataset. If the data object
13622 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13623 * the data set size</em>
13625 getCount : function(){
13626 return this.data.length || 0;
13630 * Gets the total number of records in the dataset as returned by the server.
13632 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13633 * the dataset size</em>
13635 getTotalCount : function(){
13636 return this.totalLength || 0;
13640 * Returns the sort state of the Store as an object with two properties:
13642 field {String} The name of the field by which the Records are sorted
13643 direction {String} The sort order, "ASC" or "DESC"
13646 getSortState : function(){
13647 return this.sortInfo;
13651 applySort : function(){
13652 if(this.sortInfo && !this.remoteSort){
13653 var s = this.sortInfo, f = s.field;
13654 var st = this.fields.get(f).sortType;
13655 var fn = function(r1, r2){
13656 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13657 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13659 this.data.sort(s.direction, fn);
13660 if(this.snapshot && this.snapshot != this.data){
13661 this.snapshot.sort(s.direction, fn);
13667 * Sets the default sort column and order to be used by the next load operation.
13668 * @param {String} fieldName The name of the field to sort by.
13669 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13671 setDefaultSort : function(field, dir){
13672 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13676 * Sort the Records.
13677 * If remote sorting is used, the sort is performed on the server, and the cache is
13678 * reloaded. If local sorting is used, the cache is sorted internally.
13679 * @param {String} fieldName The name of the field to sort by.
13680 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13682 sort : function(fieldName, dir){
13683 var f = this.fields.get(fieldName);
13685 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13687 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13688 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13693 this.sortToggle[f.name] = dir;
13694 this.sortInfo = {field: f.name, direction: dir};
13695 if(!this.remoteSort){
13697 this.fireEvent("datachanged", this);
13699 this.load(this.lastOptions);
13704 * Calls the specified function for each of the Records in the cache.
13705 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13706 * Returning <em>false</em> aborts and exits the iteration.
13707 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13709 each : function(fn, scope){
13710 this.data.each(fn, scope);
13714 * Gets all records modified since the last commit. Modified records are persisted across load operations
13715 * (e.g., during paging).
13716 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13718 getModifiedRecords : function(){
13719 return this.modified;
13723 createFilterFn : function(property, value, anyMatch){
13724 if(!value.exec){ // not a regex
13725 value = String(value);
13726 if(value.length == 0){
13729 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13731 return function(r){
13732 return value.test(r.data[property]);
13737 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13738 * @param {String} property A field on your records
13739 * @param {Number} start The record index to start at (defaults to 0)
13740 * @param {Number} end The last record index to include (defaults to length - 1)
13741 * @return {Number} The sum
13743 sum : function(property, start, end){
13744 var rs = this.data.items, v = 0;
13745 start = start || 0;
13746 end = (end || end === 0) ? end : rs.length-1;
13748 for(var i = start; i <= end; i++){
13749 v += (rs[i].data[property] || 0);
13755 * Filter the records by a specified property.
13756 * @param {String} field A field on your records
13757 * @param {String/RegExp} value Either a string that the field
13758 * should start with or a RegExp to test against the field
13759 * @param {Boolean} anyMatch True to match any part not just the beginning
13761 filter : function(property, value, anyMatch){
13762 var fn = this.createFilterFn(property, value, anyMatch);
13763 return fn ? this.filterBy(fn) : this.clearFilter();
13767 * Filter by a function. The specified function will be called with each
13768 * record in this data source. If the function returns true the record is included,
13769 * otherwise it is filtered.
13770 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13771 * @param {Object} scope (optional) The scope of the function (defaults to this)
13773 filterBy : function(fn, scope){
13774 this.snapshot = this.snapshot || this.data;
13775 this.data = this.queryBy(fn, scope||this);
13776 this.fireEvent("datachanged", this);
13780 * Query the records by a specified property.
13781 * @param {String} field A field on your records
13782 * @param {String/RegExp} value Either a string that the field
13783 * should start with or a RegExp to test against the field
13784 * @param {Boolean} anyMatch True to match any part not just the beginning
13785 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13787 query : function(property, value, anyMatch){
13788 var fn = this.createFilterFn(property, value, anyMatch);
13789 return fn ? this.queryBy(fn) : this.data.clone();
13793 * Query by a function. The specified function will be called with each
13794 * record in this data source. If the function returns true the record is included
13796 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13797 * @param {Object} scope (optional) The scope of the function (defaults to this)
13798 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13800 queryBy : function(fn, scope){
13801 var data = this.snapshot || this.data;
13802 return data.filterBy(fn, scope||this);
13806 * Collects unique values for a particular dataIndex from this store.
13807 * @param {String} dataIndex The property to collect
13808 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13809 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13810 * @return {Array} An array of the unique values
13812 collect : function(dataIndex, allowNull, bypassFilter){
13813 var d = (bypassFilter === true && this.snapshot) ?
13814 this.snapshot.items : this.data.items;
13815 var v, sv, r = [], l = {};
13816 for(var i = 0, len = d.length; i < len; i++){
13817 v = d[i].data[dataIndex];
13819 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13828 * Revert to a view of the Record cache with no filtering applied.
13829 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13831 clearFilter : function(suppressEvent){
13832 if(this.snapshot && this.snapshot != this.data){
13833 this.data = this.snapshot;
13834 delete this.snapshot;
13835 if(suppressEvent !== true){
13836 this.fireEvent("datachanged", this);
13842 afterEdit : function(record){
13843 if(this.modified.indexOf(record) == -1){
13844 this.modified.push(record);
13846 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13850 afterReject : function(record){
13851 this.modified.remove(record);
13852 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13856 afterCommit : function(record){
13857 this.modified.remove(record);
13858 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13862 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13863 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13865 commitChanges : function(){
13866 var m = this.modified.slice(0);
13867 this.modified = [];
13868 for(var i = 0, len = m.length; i < len; i++){
13874 * Cancel outstanding changes on all changed records.
13876 rejectChanges : function(){
13877 var m = this.modified.slice(0);
13878 this.modified = [];
13879 for(var i = 0, len = m.length; i < len; i++){
13884 onMetaChange : function(meta, rtype, o){
13885 this.recordType = rtype;
13886 this.fields = rtype.prototype.fields;
13887 delete this.snapshot;
13888 this.sortInfo = meta.sortInfo || this.sortInfo;
13889 this.modified = [];
13890 this.fireEvent('metachange', this, this.reader.meta);
13893 moveIndex : function(data, type)
13895 var index = this.indexOf(data);
13897 var newIndex = index + type;
13901 this.insert(newIndex, data);
13906 * Ext JS Library 1.1.1
13907 * Copyright(c) 2006-2007, Ext JS, LLC.
13909 * Originally Released Under LGPL - original licence link has changed is not relivant.
13912 * <script type="text/javascript">
13916 * @class Roo.data.SimpleStore
13917 * @extends Roo.data.Store
13918 * Small helper class to make creating Stores from Array data easier.
13919 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13920 * @cfg {Array} fields An array of field definition objects, or field name strings.
13921 * @cfg {Object} an existing reader (eg. copied from another store)
13922 * @cfg {Array} data The multi-dimensional array of data
13924 * @param {Object} config
13926 Roo.data.SimpleStore = function(config)
13928 Roo.data.SimpleStore.superclass.constructor.call(this, {
13930 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13933 Roo.data.Record.create(config.fields)
13935 proxy : new Roo.data.MemoryProxy(config.data)
13939 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13941 * Ext JS Library 1.1.1
13942 * Copyright(c) 2006-2007, Ext JS, LLC.
13944 * Originally Released Under LGPL - original licence link has changed is not relivant.
13947 * <script type="text/javascript">
13952 * @extends Roo.data.Store
13953 * @class Roo.data.JsonStore
13954 * Small helper class to make creating Stores for JSON data easier. <br/>
13956 var store = new Roo.data.JsonStore({
13957 url: 'get-images.php',
13959 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13962 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13963 * JsonReader and HttpProxy (unless inline data is provided).</b>
13964 * @cfg {Array} fields An array of field definition objects, or field name strings.
13966 * @param {Object} config
13968 Roo.data.JsonStore = function(c){
13969 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13970 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13971 reader: new Roo.data.JsonReader(c, c.fields)
13974 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13976 * Ext JS Library 1.1.1
13977 * Copyright(c) 2006-2007, Ext JS, LLC.
13979 * Originally Released Under LGPL - original licence link has changed is not relivant.
13982 * <script type="text/javascript">
13986 Roo.data.Field = function(config){
13987 if(typeof config == "string"){
13988 config = {name: config};
13990 Roo.apply(this, config);
13993 this.type = "auto";
13996 var st = Roo.data.SortTypes;
13997 // named sortTypes are supported, here we look them up
13998 if(typeof this.sortType == "string"){
13999 this.sortType = st[this.sortType];
14002 // set default sortType for strings and dates
14003 if(!this.sortType){
14006 this.sortType = st.asUCString;
14009 this.sortType = st.asDate;
14012 this.sortType = st.none;
14017 var stripRe = /[\$,%]/g;
14019 // prebuilt conversion function for this field, instead of
14020 // switching every time we're reading a value
14022 var cv, dateFormat = this.dateFormat;
14027 cv = function(v){ return v; };
14030 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14034 return v !== undefined && v !== null && v !== '' ?
14035 parseInt(String(v).replace(stripRe, ""), 10) : '';
14040 return v !== undefined && v !== null && v !== '' ?
14041 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14046 cv = function(v){ return v === true || v === "true" || v == 1; };
14053 if(v instanceof Date){
14057 if(dateFormat == "timestamp"){
14058 return new Date(v*1000);
14060 return Date.parseDate(v, dateFormat);
14062 var parsed = Date.parse(v);
14063 return parsed ? new Date(parsed) : null;
14072 Roo.data.Field.prototype = {
14080 * Ext JS Library 1.1.1
14081 * Copyright(c) 2006-2007, Ext JS, LLC.
14083 * Originally Released Under LGPL - original licence link has changed is not relivant.
14086 * <script type="text/javascript">
14089 // Base class for reading structured data from a data source. This class is intended to be
14090 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14093 * @class Roo.data.DataReader
14094 * Base class for reading structured data from a data source. This class is intended to be
14095 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14098 Roo.data.DataReader = function(meta, recordType){
14102 this.recordType = recordType instanceof Array ?
14103 Roo.data.Record.create(recordType) : recordType;
14106 Roo.data.DataReader.prototype = {
14109 readerType : 'Data',
14111 * Create an empty record
14112 * @param {Object} data (optional) - overlay some values
14113 * @return {Roo.data.Record} record created.
14115 newRow : function(d) {
14117 this.recordType.prototype.fields.each(function(c) {
14119 case 'int' : da[c.name] = 0; break;
14120 case 'date' : da[c.name] = new Date(); break;
14121 case 'float' : da[c.name] = 0.0; break;
14122 case 'boolean' : da[c.name] = false; break;
14123 default : da[c.name] = ""; break;
14127 return new this.recordType(Roo.apply(da, d));
14133 * Ext JS Library 1.1.1
14134 * Copyright(c) 2006-2007, Ext JS, LLC.
14136 * Originally Released Under LGPL - original licence link has changed is not relivant.
14139 * <script type="text/javascript">
14143 * @class Roo.data.DataProxy
14144 * @extends Roo.data.Observable
14145 * This class is an abstract base class for implementations which provide retrieval of
14146 * unformatted data objects.<br>
14148 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14149 * (of the appropriate type which knows how to parse the data object) to provide a block of
14150 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14152 * Custom implementations must implement the load method as described in
14153 * {@link Roo.data.HttpProxy#load}.
14155 Roo.data.DataProxy = function(){
14158 * @event beforeload
14159 * Fires before a network request is made to retrieve a data object.
14160 * @param {Object} This DataProxy object.
14161 * @param {Object} params The params parameter to the load function.
14166 * Fires before the load method's callback is called.
14167 * @param {Object} This DataProxy object.
14168 * @param {Object} o The data object.
14169 * @param {Object} arg The callback argument object passed to the load function.
14173 * @event loadexception
14174 * Fires if an Exception occurs during data retrieval.
14175 * @param {Object} This DataProxy object.
14176 * @param {Object} o The data object.
14177 * @param {Object} arg The callback argument object passed to the load function.
14178 * @param {Object} e The Exception.
14180 loadexception : true
14182 Roo.data.DataProxy.superclass.constructor.call(this);
14185 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14188 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14192 * Ext JS Library 1.1.1
14193 * Copyright(c) 2006-2007, Ext JS, LLC.
14195 * Originally Released Under LGPL - original licence link has changed is not relivant.
14198 * <script type="text/javascript">
14201 * @class Roo.data.MemoryProxy
14202 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14203 * to the Reader when its load method is called.
14205 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14207 Roo.data.MemoryProxy = function(data){
14211 Roo.data.MemoryProxy.superclass.constructor.call(this);
14215 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14218 * Load data from the requested source (in this case an in-memory
14219 * data object passed to the constructor), read the data object into
14220 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14221 * process that block using the passed callback.
14222 * @param {Object} params This parameter is not used by the MemoryProxy class.
14223 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14224 * object into a block of Roo.data.Records.
14225 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14226 * The function must be passed <ul>
14227 * <li>The Record block object</li>
14228 * <li>The "arg" argument from the load function</li>
14229 * <li>A boolean success indicator</li>
14231 * @param {Object} scope The scope in which to call the callback
14232 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14234 load : function(params, reader, callback, scope, arg){
14235 params = params || {};
14238 result = reader.readRecords(params.data ? params.data :this.data);
14240 this.fireEvent("loadexception", this, arg, null, e);
14241 callback.call(scope, null, arg, false);
14244 callback.call(scope, result, arg, true);
14248 update : function(params, records){
14253 * Ext JS Library 1.1.1
14254 * Copyright(c) 2006-2007, Ext JS, LLC.
14256 * Originally Released Under LGPL - original licence link has changed is not relivant.
14259 * <script type="text/javascript">
14262 * @class Roo.data.HttpProxy
14263 * @extends Roo.data.DataProxy
14264 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14265 * configured to reference a certain URL.<br><br>
14267 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14268 * from which the running page was served.<br><br>
14270 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14272 * Be aware that to enable the browser to parse an XML document, the server must set
14273 * the Content-Type header in the HTTP response to "text/xml".
14275 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14276 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14277 * will be used to make the request.
14279 Roo.data.HttpProxy = function(conn){
14280 Roo.data.HttpProxy.superclass.constructor.call(this);
14281 // is conn a conn config or a real conn?
14283 this.useAjax = !conn || !conn.events;
14287 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14288 // thse are take from connection...
14291 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14294 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14295 * extra parameters to each request made by this object. (defaults to undefined)
14298 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14299 * to each request made by this object. (defaults to undefined)
14302 * @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)
14305 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14308 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14314 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14318 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14319 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14320 * a finer-grained basis than the DataProxy events.
14322 getConnection : function(){
14323 return this.useAjax ? Roo.Ajax : this.conn;
14327 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14328 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14329 * process that block using the passed callback.
14330 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14331 * for the request to the remote server.
14332 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14333 * object into a block of Roo.data.Records.
14334 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14335 * The function must be passed <ul>
14336 * <li>The Record block object</li>
14337 * <li>The "arg" argument from the load function</li>
14338 * <li>A boolean success indicator</li>
14340 * @param {Object} scope The scope in which to call the callback
14341 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14343 load : function(params, reader, callback, scope, arg){
14344 if(this.fireEvent("beforeload", this, params) !== false){
14346 params : params || {},
14348 callback : callback,
14353 callback : this.loadResponse,
14357 Roo.applyIf(o, this.conn);
14358 if(this.activeRequest){
14359 Roo.Ajax.abort(this.activeRequest);
14361 this.activeRequest = Roo.Ajax.request(o);
14363 this.conn.request(o);
14366 callback.call(scope||this, null, arg, false);
14371 loadResponse : function(o, success, response){
14372 delete this.activeRequest;
14374 this.fireEvent("loadexception", this, o, response);
14375 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14380 result = o.reader.read(response);
14382 this.fireEvent("loadexception", this, o, response, e);
14383 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14387 this.fireEvent("load", this, o, o.request.arg);
14388 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14392 update : function(dataSet){
14397 updateResponse : function(dataSet){
14402 * Ext JS Library 1.1.1
14403 * Copyright(c) 2006-2007, Ext JS, LLC.
14405 * Originally Released Under LGPL - original licence link has changed is not relivant.
14408 * <script type="text/javascript">
14412 * @class Roo.data.ScriptTagProxy
14413 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14414 * other than the originating domain of the running page.<br><br>
14416 * <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
14417 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14419 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14420 * source code that is used as the source inside a <script> tag.<br><br>
14422 * In order for the browser to process the returned data, the server must wrap the data object
14423 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14424 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14425 * depending on whether the callback name was passed:
14428 boolean scriptTag = false;
14429 String cb = request.getParameter("callback");
14432 response.setContentType("text/javascript");
14434 response.setContentType("application/x-json");
14436 Writer out = response.getWriter();
14438 out.write(cb + "(");
14440 out.print(dataBlock.toJsonString());
14447 * @param {Object} config A configuration object.
14449 Roo.data.ScriptTagProxy = function(config){
14450 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14451 Roo.apply(this, config);
14452 this.head = document.getElementsByTagName("head")[0];
14455 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14457 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14459 * @cfg {String} url The URL from which to request the data object.
14462 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14466 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14467 * the server the name of the callback function set up by the load call to process the returned data object.
14468 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14469 * javascript output which calls this named function passing the data object as its only parameter.
14471 callbackParam : "callback",
14473 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14474 * name to the request.
14479 * Load data from the configured URL, read the data object into
14480 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14481 * process that block using the passed callback.
14482 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14483 * for the request to the remote server.
14484 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14485 * object into a block of Roo.data.Records.
14486 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14487 * The function must be passed <ul>
14488 * <li>The Record block object</li>
14489 * <li>The "arg" argument from the load function</li>
14490 * <li>A boolean success indicator</li>
14492 * @param {Object} scope The scope in which to call the callback
14493 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14495 load : function(params, reader, callback, scope, arg){
14496 if(this.fireEvent("beforeload", this, params) !== false){
14498 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14500 var url = this.url;
14501 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14503 url += "&_dc=" + (new Date().getTime());
14505 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14508 cb : "stcCallback"+transId,
14509 scriptId : "stcScript"+transId,
14513 callback : callback,
14519 window[trans.cb] = function(o){
14520 conn.handleResponse(o, trans);
14523 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14525 if(this.autoAbort !== false){
14529 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14531 var script = document.createElement("script");
14532 script.setAttribute("src", url);
14533 script.setAttribute("type", "text/javascript");
14534 script.setAttribute("id", trans.scriptId);
14535 this.head.appendChild(script);
14537 this.trans = trans;
14539 callback.call(scope||this, null, arg, false);
14544 isLoading : function(){
14545 return this.trans ? true : false;
14549 * Abort the current server request.
14551 abort : function(){
14552 if(this.isLoading()){
14553 this.destroyTrans(this.trans);
14558 destroyTrans : function(trans, isLoaded){
14559 this.head.removeChild(document.getElementById(trans.scriptId));
14560 clearTimeout(trans.timeoutId);
14562 window[trans.cb] = undefined;
14564 delete window[trans.cb];
14567 // if hasn't been loaded, wait for load to remove it to prevent script error
14568 window[trans.cb] = function(){
14569 window[trans.cb] = undefined;
14571 delete window[trans.cb];
14578 handleResponse : function(o, trans){
14579 this.trans = false;
14580 this.destroyTrans(trans, true);
14583 result = trans.reader.readRecords(o);
14585 this.fireEvent("loadexception", this, o, trans.arg, e);
14586 trans.callback.call(trans.scope||window, null, trans.arg, false);
14589 this.fireEvent("load", this, o, trans.arg);
14590 trans.callback.call(trans.scope||window, result, trans.arg, true);
14594 handleFailure : function(trans){
14595 this.trans = false;
14596 this.destroyTrans(trans, false);
14597 this.fireEvent("loadexception", this, null, trans.arg);
14598 trans.callback.call(trans.scope||window, null, trans.arg, false);
14602 * Ext JS Library 1.1.1
14603 * Copyright(c) 2006-2007, Ext JS, LLC.
14605 * Originally Released Under LGPL - original licence link has changed is not relivant.
14608 * <script type="text/javascript">
14612 * @class Roo.data.JsonReader
14613 * @extends Roo.data.DataReader
14614 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14615 * based on mappings in a provided Roo.data.Record constructor.
14617 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14618 * in the reply previously.
14623 var RecordDef = Roo.data.Record.create([
14624 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14625 {name: 'occupation'} // This field will use "occupation" as the mapping.
14627 var myReader = new Roo.data.JsonReader({
14628 totalProperty: "results", // The property which contains the total dataset size (optional)
14629 root: "rows", // The property which contains an Array of row objects
14630 id: "id" // The property within each row object that provides an ID for the record (optional)
14634 * This would consume a JSON file like this:
14636 { 'results': 2, 'rows': [
14637 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14638 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14641 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14642 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14643 * paged from the remote server.
14644 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14645 * @cfg {String} root name of the property which contains the Array of row objects.
14646 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14647 * @cfg {Array} fields Array of field definition objects
14649 * Create a new JsonReader
14650 * @param {Object} meta Metadata configuration options
14651 * @param {Object} recordType Either an Array of field definition objects,
14652 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14654 Roo.data.JsonReader = function(meta, recordType){
14657 // set some defaults:
14658 Roo.applyIf(meta, {
14659 totalProperty: 'total',
14660 successProperty : 'success',
14665 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14667 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14669 readerType : 'Json',
14672 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14673 * Used by Store query builder to append _requestMeta to params.
14676 metaFromRemote : false,
14678 * This method is only used by a DataProxy which has retrieved data from a remote server.
14679 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14680 * @return {Object} data A data block which is used by an Roo.data.Store object as
14681 * a cache of Roo.data.Records.
14683 read : function(response){
14684 var json = response.responseText;
14686 var o = /* eval:var:o */ eval("("+json+")");
14688 throw {message: "JsonReader.read: Json object not found"};
14694 this.metaFromRemote = true;
14695 this.meta = o.metaData;
14696 this.recordType = Roo.data.Record.create(o.metaData.fields);
14697 this.onMetaChange(this.meta, this.recordType, o);
14699 return this.readRecords(o);
14702 // private function a store will implement
14703 onMetaChange : function(meta, recordType, o){
14710 simpleAccess: function(obj, subsc) {
14717 getJsonAccessor: function(){
14719 return function(expr) {
14721 return(re.test(expr))
14722 ? new Function("obj", "return obj." + expr)
14727 return Roo.emptyFn;
14732 * Create a data block containing Roo.data.Records from an XML document.
14733 * @param {Object} o An object which contains an Array of row objects in the property specified
14734 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14735 * which contains the total size of the dataset.
14736 * @return {Object} data A data block which is used by an Roo.data.Store object as
14737 * a cache of Roo.data.Records.
14739 readRecords : function(o){
14741 * After any data loads, the raw JSON data is available for further custom processing.
14745 var s = this.meta, Record = this.recordType,
14746 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14748 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14750 if(s.totalProperty) {
14751 this.getTotal = this.getJsonAccessor(s.totalProperty);
14753 if(s.successProperty) {
14754 this.getSuccess = this.getJsonAccessor(s.successProperty);
14756 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14758 var g = this.getJsonAccessor(s.id);
14759 this.getId = function(rec) {
14761 return (r === undefined || r === "") ? null : r;
14764 this.getId = function(){return null;};
14767 for(var jj = 0; jj < fl; jj++){
14769 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14770 this.ef[jj] = this.getJsonAccessor(map);
14774 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14775 if(s.totalProperty){
14776 var vt = parseInt(this.getTotal(o), 10);
14781 if(s.successProperty){
14782 var vs = this.getSuccess(o);
14783 if(vs === false || vs === 'false'){
14788 for(var i = 0; i < c; i++){
14791 var id = this.getId(n);
14792 for(var j = 0; j < fl; j++){
14794 var v = this.ef[j](n);
14796 Roo.log('missing convert for ' + f.name);
14800 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14802 var record = new Record(values, id);
14804 records[i] = record;
14810 totalRecords : totalRecords
14813 // used when loading children.. @see loadDataFromChildren
14814 toLoadData: function(rec)
14816 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14817 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14818 return { data : data, total : data.length };
14823 * Ext JS Library 1.1.1
14824 * Copyright(c) 2006-2007, Ext JS, LLC.
14826 * Originally Released Under LGPL - original licence link has changed is not relivant.
14829 * <script type="text/javascript">
14833 * @class Roo.data.ArrayReader
14834 * @extends Roo.data.DataReader
14835 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14836 * Each element of that Array represents a row of data fields. The
14837 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14838 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14842 var RecordDef = Roo.data.Record.create([
14843 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14844 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14846 var myReader = new Roo.data.ArrayReader({
14847 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14851 * This would consume an Array like this:
14853 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14857 * Create a new JsonReader
14858 * @param {Object} meta Metadata configuration options.
14859 * @param {Object|Array} recordType Either an Array of field definition objects
14861 * @cfg {Array} fields Array of field definition objects
14862 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14863 * as specified to {@link Roo.data.Record#create},
14864 * or an {@link Roo.data.Record} object
14867 * created using {@link Roo.data.Record#create}.
14869 Roo.data.ArrayReader = function(meta, recordType)
14871 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14874 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14877 * Create a data block containing Roo.data.Records from an XML document.
14878 * @param {Object} o An Array of row objects which represents the dataset.
14879 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14880 * a cache of Roo.data.Records.
14882 readRecords : function(o)
14884 var sid = this.meta ? this.meta.id : null;
14885 var recordType = this.recordType, fields = recordType.prototype.fields;
14888 for(var i = 0; i < root.length; i++){
14891 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14892 for(var j = 0, jlen = fields.length; j < jlen; j++){
14893 var f = fields.items[j];
14894 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14895 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14897 values[f.name] = v;
14899 var record = new recordType(values, id);
14901 records[records.length] = record;
14905 totalRecords : records.length
14908 // used when loading children.. @see loadDataFromChildren
14909 toLoadData: function(rec)
14911 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14912 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14923 * @class Roo.bootstrap.ComboBox
14924 * @extends Roo.bootstrap.TriggerField
14925 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14926 * @cfg {Boolean} append (true|false) default false
14927 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14928 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14929 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14930 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14931 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14932 * @cfg {Boolean} animate default true
14933 * @cfg {Boolean} emptyResultText only for touch device
14934 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14935 * @cfg {String} emptyTitle default ''
14936 * @cfg {Number} width fixed with? experimental
14938 * Create a new ComboBox.
14939 * @param {Object} config Configuration options
14941 Roo.bootstrap.ComboBox = function(config){
14942 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14946 * Fires when the dropdown list is expanded
14947 * @param {Roo.bootstrap.ComboBox} combo This combo box
14952 * Fires when the dropdown list is collapsed
14953 * @param {Roo.bootstrap.ComboBox} combo This combo box
14957 * @event beforeselect
14958 * Fires before a list item is selected. Return false to cancel the selection.
14959 * @param {Roo.bootstrap.ComboBox} combo This combo box
14960 * @param {Roo.data.Record} record The data record returned from the underlying store
14961 * @param {Number} index The index of the selected item in the dropdown list
14963 'beforeselect' : true,
14966 * Fires when a list item is selected
14967 * @param {Roo.bootstrap.ComboBox} combo This combo box
14968 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14969 * @param {Number} index The index of the selected item in the dropdown list
14973 * @event beforequery
14974 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14975 * The event object passed has these properties:
14976 * @param {Roo.bootstrap.ComboBox} combo This combo box
14977 * @param {String} query The query
14978 * @param {Boolean} forceAll true to force "all" query
14979 * @param {Boolean} cancel true to cancel the query
14980 * @param {Object} e The query event object
14982 'beforequery': true,
14985 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14986 * @param {Roo.bootstrap.ComboBox} combo This combo box
14991 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14992 * @param {Roo.bootstrap.ComboBox} combo This combo box
14993 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14998 * Fires when the remove value from the combobox array
14999 * @param {Roo.bootstrap.ComboBox} combo This combo box
15003 * @event afterremove
15004 * Fires when the remove value from the combobox array
15005 * @param {Roo.bootstrap.ComboBox} combo This combo box
15007 'afterremove' : true,
15009 * @event specialfilter
15010 * Fires when specialfilter
15011 * @param {Roo.bootstrap.ComboBox} combo This combo box
15013 'specialfilter' : true,
15016 * Fires when tick the element
15017 * @param {Roo.bootstrap.ComboBox} combo This combo box
15021 * @event touchviewdisplay
15022 * Fires when touch view require special display (default is using displayField)
15023 * @param {Roo.bootstrap.ComboBox} combo This combo box
15024 * @param {Object} cfg set html .
15026 'touchviewdisplay' : true
15031 this.tickItems = [];
15033 this.selectedIndex = -1;
15034 if(this.mode == 'local'){
15035 if(config.queryDelay === undefined){
15036 this.queryDelay = 10;
15038 if(config.minChars === undefined){
15044 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15047 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15048 * rendering into an Roo.Editor, defaults to false)
15051 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15052 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15055 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15058 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15059 * the dropdown list (defaults to undefined, with no header element)
15063 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15067 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15069 listWidth: undefined,
15071 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15072 * mode = 'remote' or 'text' if mode = 'local')
15074 displayField: undefined,
15077 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15078 * mode = 'remote' or 'value' if mode = 'local').
15079 * Note: use of a valueField requires the user make a selection
15080 * in order for a value to be mapped.
15082 valueField: undefined,
15084 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15089 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15090 * field's data value (defaults to the underlying DOM element's name)
15092 hiddenName: undefined,
15094 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15098 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15100 selectedClass: 'active',
15103 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15107 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15108 * anchor positions (defaults to 'tl-bl')
15110 listAlign: 'tl-bl?',
15112 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15116 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15117 * query specified by the allQuery config option (defaults to 'query')
15119 triggerAction: 'query',
15121 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15122 * (defaults to 4, does not apply if editable = false)
15126 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15127 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15131 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15132 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15136 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15137 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15141 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15142 * when editable = true (defaults to false)
15144 selectOnFocus:false,
15146 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15148 queryParam: 'query',
15150 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15151 * when mode = 'remote' (defaults to 'Loading...')
15153 loadingText: 'Loading...',
15155 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15159 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15163 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15164 * traditional select (defaults to true)
15168 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15172 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15176 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15177 * listWidth has a higher value)
15181 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15182 * allow the user to set arbitrary text into the field (defaults to false)
15184 forceSelection:false,
15186 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15187 * if typeAhead = true (defaults to 250)
15189 typeAheadDelay : 250,
15191 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15192 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15194 valueNotFoundText : undefined,
15196 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15198 blockFocus : false,
15201 * @cfg {Boolean} disableClear Disable showing of clear button.
15203 disableClear : false,
15205 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15207 alwaysQuery : false,
15210 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15215 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15217 invalidClass : "has-warning",
15220 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15222 validClass : "has-success",
15225 * @cfg {Boolean} specialFilter (true|false) special filter default false
15227 specialFilter : false,
15230 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15232 mobileTouchView : true,
15235 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15237 useNativeIOS : false,
15240 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15242 mobile_restrict_height : false,
15244 ios_options : false,
15256 btnPosition : 'right',
15257 triggerList : true,
15258 showToggleBtn : true,
15260 emptyResultText: 'Empty',
15261 triggerText : 'Select',
15265 // element that contains real text value.. (when hidden is used..)
15267 getAutoCreate : function()
15272 * Render classic select for iso
15275 if(Roo.isIOS && this.useNativeIOS){
15276 cfg = this.getAutoCreateNativeIOS();
15284 if(Roo.isTouch && this.mobileTouchView){
15285 cfg = this.getAutoCreateTouchView();
15292 if(!this.tickable){
15293 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15298 * ComboBox with tickable selections
15301 var align = this.labelAlign || this.parentLabelAlign();
15304 cls : 'form-group roo-combobox-tickable' //input-group
15307 var btn_text_select = '';
15308 var btn_text_done = '';
15309 var btn_text_cancel = '';
15311 if (this.btn_text_show) {
15312 btn_text_select = 'Select';
15313 btn_text_done = 'Done';
15314 btn_text_cancel = 'Cancel';
15319 cls : 'tickable-buttons',
15324 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15325 //html : this.triggerText
15326 html: btn_text_select
15332 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15334 html: btn_text_done
15340 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15342 html: btn_text_cancel
15348 buttons.cn.unshift({
15350 cls: 'roo-select2-search-field-input'
15356 Roo.each(buttons.cn, function(c){
15358 c.cls += ' btn-' + _this.size;
15361 if (_this.disabled) {
15368 style : 'display: contents',
15373 cls: 'form-hidden-field'
15377 cls: 'roo-select2-choices',
15381 cls: 'roo-select2-search-field',
15392 cls: 'roo-select2-container input-group roo-select2-container-multi',
15398 // cls: 'typeahead typeahead-long dropdown-menu',
15399 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15404 if(this.hasFeedback && !this.allowBlank){
15408 cls: 'glyphicon form-control-feedback'
15411 combobox.cn.push(feedback);
15418 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15419 tooltip : 'This field is required'
15421 if (Roo.bootstrap.version == 4) {
15424 style : 'display:none'
15427 if (align ==='left' && this.fieldLabel.length) {
15429 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15436 cls : 'control-label col-form-label',
15437 html : this.fieldLabel
15449 var labelCfg = cfg.cn[1];
15450 var contentCfg = cfg.cn[2];
15453 if(this.indicatorpos == 'right'){
15459 cls : 'control-label col-form-label',
15463 html : this.fieldLabel
15479 labelCfg = cfg.cn[0];
15480 contentCfg = cfg.cn[1];
15484 if(this.labelWidth > 12){
15485 labelCfg.style = "width: " + this.labelWidth + 'px';
15487 if(this.width * 1 > 0){
15488 contentCfg.style = "width: " + this.width + 'px';
15490 if(this.labelWidth < 13 && this.labelmd == 0){
15491 this.labelmd = this.labelWidth;
15494 if(this.labellg > 0){
15495 labelCfg.cls += ' col-lg-' + this.labellg;
15496 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15499 if(this.labelmd > 0){
15500 labelCfg.cls += ' col-md-' + this.labelmd;
15501 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15504 if(this.labelsm > 0){
15505 labelCfg.cls += ' col-sm-' + this.labelsm;
15506 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15509 if(this.labelxs > 0){
15510 labelCfg.cls += ' col-xs-' + this.labelxs;
15511 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15515 } else if ( this.fieldLabel.length) {
15516 // Roo.log(" label");
15521 //cls : 'input-group-addon',
15522 html : this.fieldLabel
15527 if(this.indicatorpos == 'right'){
15531 //cls : 'input-group-addon',
15532 html : this.fieldLabel
15542 // Roo.log(" no label && no align");
15549 ['xs','sm','md','lg'].map(function(size){
15550 if (settings[size]) {
15551 cfg.cls += ' col-' + size + '-' + settings[size];
15559 _initEventsCalled : false,
15562 initEvents: function()
15564 if (this._initEventsCalled) { // as we call render... prevent looping...
15567 this._initEventsCalled = true;
15570 throw "can not find store for combo";
15573 this.indicator = this.indicatorEl();
15575 this.store = Roo.factory(this.store, Roo.data);
15576 this.store.parent = this;
15578 // if we are building from html. then this element is so complex, that we can not really
15579 // use the rendered HTML.
15580 // so we have to trash and replace the previous code.
15581 if (Roo.XComponent.build_from_html) {
15582 // remove this element....
15583 var e = this.el.dom, k=0;
15584 while (e ) { e = e.previousSibling; ++k;}
15589 this.rendered = false;
15591 this.render(this.parent().getChildContainer(true), k);
15594 if(Roo.isIOS && this.useNativeIOS){
15595 this.initIOSView();
15603 if(Roo.isTouch && this.mobileTouchView){
15604 this.initTouchView();
15609 this.initTickableEvents();
15613 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15615 if(this.hiddenName){
15617 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15619 this.hiddenField.dom.value =
15620 this.hiddenValue !== undefined ? this.hiddenValue :
15621 this.value !== undefined ? this.value : '';
15623 // prevent input submission
15624 this.el.dom.removeAttribute('name');
15625 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15630 // this.el.dom.setAttribute('autocomplete', 'off');
15633 var cls = 'x-combo-list';
15635 //this.list = new Roo.Layer({
15636 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15642 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15643 _this.list.setWidth(lw);
15646 this.list.on('mouseover', this.onViewOver, this);
15647 this.list.on('mousemove', this.onViewMove, this);
15648 this.list.on('scroll', this.onViewScroll, this);
15651 this.list.swallowEvent('mousewheel');
15652 this.assetHeight = 0;
15655 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15656 this.assetHeight += this.header.getHeight();
15659 this.innerList = this.list.createChild({cls:cls+'-inner'});
15660 this.innerList.on('mouseover', this.onViewOver, this);
15661 this.innerList.on('mousemove', this.onViewMove, this);
15662 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15664 if(this.allowBlank && !this.pageSize && !this.disableClear){
15665 this.footer = this.list.createChild({cls:cls+'-ft'});
15666 this.pageTb = new Roo.Toolbar(this.footer);
15670 this.footer = this.list.createChild({cls:cls+'-ft'});
15671 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15672 {pageSize: this.pageSize});
15676 if (this.pageTb && this.allowBlank && !this.disableClear) {
15678 this.pageTb.add(new Roo.Toolbar.Fill(), {
15679 cls: 'x-btn-icon x-btn-clear',
15681 handler: function()
15684 _this.clearValue();
15685 _this.onSelect(false, -1);
15690 this.assetHeight += this.footer.getHeight();
15695 this.tpl = Roo.bootstrap.version == 4 ?
15696 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15697 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15700 this.view = new Roo.View(this.list, this.tpl, {
15701 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15703 //this.view.wrapEl.setDisplayed(false);
15704 this.view.on('click', this.onViewClick, this);
15707 this.store.on('beforeload', this.onBeforeLoad, this);
15708 this.store.on('load', this.onLoad, this);
15709 this.store.on('loadexception', this.onLoadException, this);
15711 if(this.resizable){
15712 this.resizer = new Roo.Resizable(this.list, {
15713 pinned:true, handles:'se'
15715 this.resizer.on('resize', function(r, w, h){
15716 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15717 this.listWidth = w;
15718 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15719 this.restrictHeight();
15721 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15724 if(!this.editable){
15725 this.editable = true;
15726 this.setEditable(false);
15731 if (typeof(this.events.add.listeners) != 'undefined') {
15733 this.addicon = this.wrap.createChild(
15734 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15736 this.addicon.on('click', function(e) {
15737 this.fireEvent('add', this);
15740 if (typeof(this.events.edit.listeners) != 'undefined') {
15742 this.editicon = this.wrap.createChild(
15743 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15744 if (this.addicon) {
15745 this.editicon.setStyle('margin-left', '40px');
15747 this.editicon.on('click', function(e) {
15749 // we fire even if inothing is selected..
15750 this.fireEvent('edit', this, this.lastData );
15756 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15757 "up" : function(e){
15758 this.inKeyMode = true;
15762 "down" : function(e){
15763 if(!this.isExpanded()){
15764 this.onTriggerClick();
15766 this.inKeyMode = true;
15771 "enter" : function(e){
15772 // this.onViewClick();
15776 if(this.fireEvent("specialkey", this, e)){
15777 this.onViewClick(false);
15783 "esc" : function(e){
15787 "tab" : function(e){
15790 if(this.fireEvent("specialkey", this, e)){
15791 this.onViewClick(false);
15799 doRelay : function(foo, bar, hname){
15800 if(hname == 'down' || this.scope.isExpanded()){
15801 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15810 this.queryDelay = Math.max(this.queryDelay || 10,
15811 this.mode == 'local' ? 10 : 250);
15814 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15816 if(this.typeAhead){
15817 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15819 if(this.editable !== false){
15820 this.inputEl().on("keyup", this.onKeyUp, this);
15822 if(this.forceSelection){
15823 this.inputEl().on('blur', this.doForce, this);
15827 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15828 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15832 initTickableEvents: function()
15836 if(this.hiddenName){
15838 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15840 this.hiddenField.dom.value =
15841 this.hiddenValue !== undefined ? this.hiddenValue :
15842 this.value !== undefined ? this.value : '';
15844 // prevent input submission
15845 this.el.dom.removeAttribute('name');
15846 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15851 // this.list = this.el.select('ul.dropdown-menu',true).first();
15853 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15854 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15855 if(this.triggerList){
15856 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15859 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15860 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15862 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15863 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15865 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15866 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15868 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15869 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15870 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15873 this.cancelBtn.hide();
15878 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15879 _this.list.setWidth(lw);
15882 this.list.on('mouseover', this.onViewOver, this);
15883 this.list.on('mousemove', this.onViewMove, this);
15885 this.list.on('scroll', this.onViewScroll, this);
15888 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15889 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15892 this.view = new Roo.View(this.list, this.tpl, {
15897 selectedClass: this.selectedClass
15900 //this.view.wrapEl.setDisplayed(false);
15901 this.view.on('click', this.onViewClick, this);
15905 this.store.on('beforeload', this.onBeforeLoad, this);
15906 this.store.on('load', this.onLoad, this);
15907 this.store.on('loadexception', this.onLoadException, this);
15910 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15911 "up" : function(e){
15912 this.inKeyMode = true;
15916 "down" : function(e){
15917 this.inKeyMode = true;
15921 "enter" : function(e){
15922 if(this.fireEvent("specialkey", this, e)){
15923 this.onViewClick(false);
15929 "esc" : function(e){
15930 this.onTickableFooterButtonClick(e, false, false);
15933 "tab" : function(e){
15934 this.fireEvent("specialkey", this, e);
15936 this.onTickableFooterButtonClick(e, false, false);
15943 doRelay : function(e, fn, key){
15944 if(this.scope.isExpanded()){
15945 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15954 this.queryDelay = Math.max(this.queryDelay || 10,
15955 this.mode == 'local' ? 10 : 250);
15958 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15960 if(this.typeAhead){
15961 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15964 if(this.editable !== false){
15965 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15968 this.indicator = this.indicatorEl();
15970 if(this.indicator){
15971 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15972 this.indicator.hide();
15977 onDestroy : function(){
15979 this.view.setStore(null);
15980 this.view.el.removeAllListeners();
15981 this.view.el.remove();
15982 this.view.purgeListeners();
15985 this.list.dom.innerHTML = '';
15989 this.store.un('beforeload', this.onBeforeLoad, this);
15990 this.store.un('load', this.onLoad, this);
15991 this.store.un('loadexception', this.onLoadException, this);
15993 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15997 fireKey : function(e){
15998 if(e.isNavKeyPress() && !this.list.isVisible()){
15999 this.fireEvent("specialkey", this, e);
16004 onResize: function(w, h)
16008 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16010 // if(typeof w != 'number'){
16011 // // we do not handle it!?!?
16014 // var tw = this.trigger.getWidth();
16015 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16016 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16018 // this.inputEl().setWidth( this.adjustWidth('input', x));
16020 // //this.trigger.setStyle('left', x+'px');
16022 // if(this.list && this.listWidth === undefined){
16023 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16024 // this.list.setWidth(lw);
16025 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16033 * Allow or prevent the user from directly editing the field text. If false is passed,
16034 * the user will only be able to select from the items defined in the dropdown list. This method
16035 * is the runtime equivalent of setting the 'editable' config option at config time.
16036 * @param {Boolean} value True to allow the user to directly edit the field text
16038 setEditable : function(value){
16039 if(value == this.editable){
16042 this.editable = value;
16044 this.inputEl().dom.setAttribute('readOnly', true);
16045 this.inputEl().on('mousedown', this.onTriggerClick, this);
16046 this.inputEl().addClass('x-combo-noedit');
16048 this.inputEl().dom.setAttribute('readOnly', false);
16049 this.inputEl().un('mousedown', this.onTriggerClick, this);
16050 this.inputEl().removeClass('x-combo-noedit');
16056 onBeforeLoad : function(combo,opts){
16057 if(!this.hasFocus){
16061 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16063 this.restrictHeight();
16064 this.selectedIndex = -1;
16068 onLoad : function(){
16070 this.hasQuery = false;
16072 if(!this.hasFocus){
16076 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16077 this.loading.hide();
16080 if(this.store.getCount() > 0){
16083 this.restrictHeight();
16084 if(this.lastQuery == this.allQuery){
16085 if(this.editable && !this.tickable){
16086 this.inputEl().dom.select();
16090 !this.selectByValue(this.value, true) &&
16093 !this.store.lastOptions ||
16094 typeof(this.store.lastOptions.add) == 'undefined' ||
16095 this.store.lastOptions.add != true
16098 this.select(0, true);
16101 if(this.autoFocus){
16104 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16105 this.taTask.delay(this.typeAheadDelay);
16109 this.onEmptyResults();
16115 onLoadException : function()
16117 this.hasQuery = false;
16119 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16120 this.loading.hide();
16123 if(this.tickable && this.editable){
16128 // only causes errors at present
16129 //Roo.log(this.store.reader.jsonData);
16130 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16132 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16138 onTypeAhead : function(){
16139 if(this.store.getCount() > 0){
16140 var r = this.store.getAt(0);
16141 var newValue = r.data[this.displayField];
16142 var len = newValue.length;
16143 var selStart = this.getRawValue().length;
16145 if(selStart != len){
16146 this.setRawValue(newValue);
16147 this.selectText(selStart, newValue.length);
16153 onSelect : function(record, index){
16155 if(this.fireEvent('beforeselect', this, record, index) !== false){
16157 this.setFromData(index > -1 ? record.data : false);
16160 this.fireEvent('select', this, record, index);
16165 * Returns the currently selected field value or empty string if no value is set.
16166 * @return {String} value The selected value
16168 getValue : function()
16170 if(Roo.isIOS && this.useNativeIOS){
16171 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16175 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16178 if(this.valueField){
16179 return typeof this.value != 'undefined' ? this.value : '';
16181 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16185 getRawValue : function()
16187 if(Roo.isIOS && this.useNativeIOS){
16188 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16191 var v = this.inputEl().getValue();
16197 * Clears any text/value currently set in the field
16199 clearValue : function(){
16201 if(this.hiddenField){
16202 this.hiddenField.dom.value = '';
16205 this.setRawValue('');
16206 this.lastSelectionText = '';
16207 this.lastData = false;
16209 var close = this.closeTriggerEl();
16220 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16221 * will be displayed in the field. If the value does not match the data value of an existing item,
16222 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16223 * Otherwise the field will be blank (although the value will still be set).
16224 * @param {String} value The value to match
16226 setValue : function(v)
16228 if(Roo.isIOS && this.useNativeIOS){
16229 this.setIOSValue(v);
16239 if(this.valueField){
16240 var r = this.findRecord(this.valueField, v);
16242 text = r.data[this.displayField];
16243 }else if(this.valueNotFoundText !== undefined){
16244 text = this.valueNotFoundText;
16247 this.lastSelectionText = text;
16248 if(this.hiddenField){
16249 this.hiddenField.dom.value = v;
16251 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16254 var close = this.closeTriggerEl();
16257 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16263 * @property {Object} the last set data for the element
16268 * Sets the value of the field based on a object which is related to the record format for the store.
16269 * @param {Object} value the value to set as. or false on reset?
16271 setFromData : function(o){
16278 var dv = ''; // display value
16279 var vv = ''; // value value..
16281 if (this.displayField) {
16282 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16284 // this is an error condition!!!
16285 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16288 if(this.valueField){
16289 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16292 var close = this.closeTriggerEl();
16295 if(dv.length || vv * 1 > 0){
16297 this.blockFocus=true;
16303 if(this.hiddenField){
16304 this.hiddenField.dom.value = vv;
16306 this.lastSelectionText = dv;
16307 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16311 // no hidden field.. - we store the value in 'value', but still display
16312 // display field!!!!
16313 this.lastSelectionText = dv;
16314 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16321 reset : function(){
16322 // overridden so that last data is reset..
16329 this.setValue(this.originalValue);
16330 //this.clearInvalid();
16331 this.lastData = false;
16333 this.view.clearSelections();
16339 findRecord : function(prop, value){
16341 if(this.store.getCount() > 0){
16342 this.store.each(function(r){
16343 if(r.data[prop] == value){
16353 getName: function()
16355 // returns hidden if it's set..
16356 if (!this.rendered) {return ''};
16357 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16361 onViewMove : function(e, t){
16362 this.inKeyMode = false;
16366 onViewOver : function(e, t){
16367 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16370 var item = this.view.findItemFromChild(t);
16373 var index = this.view.indexOf(item);
16374 this.select(index, false);
16379 onViewClick : function(view, doFocus, el, e)
16381 var index = this.view.getSelectedIndexes()[0];
16383 var r = this.store.getAt(index);
16387 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16394 Roo.each(this.tickItems, function(v,k){
16396 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16398 _this.tickItems.splice(k, 1);
16400 if(typeof(e) == 'undefined' && view == false){
16401 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16413 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16414 this.tickItems.push(r.data);
16417 if(typeof(e) == 'undefined' && view == false){
16418 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16425 this.onSelect(r, index);
16427 if(doFocus !== false && !this.blockFocus){
16428 this.inputEl().focus();
16433 restrictHeight : function(){
16434 //this.innerList.dom.style.height = '';
16435 //var inner = this.innerList.dom;
16436 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16437 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16438 //this.list.beginUpdate();
16439 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16440 this.list.alignTo(this.inputEl(), this.listAlign);
16441 this.list.alignTo(this.inputEl(), this.listAlign);
16442 //this.list.endUpdate();
16446 onEmptyResults : function(){
16448 if(this.tickable && this.editable){
16449 this.hasFocus = false;
16450 this.restrictHeight();
16458 * Returns true if the dropdown list is expanded, else false.
16460 isExpanded : function(){
16461 return this.list.isVisible();
16465 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16466 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16467 * @param {String} value The data value of the item to select
16468 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16469 * selected item if it is not currently in view (defaults to true)
16470 * @return {Boolean} True if the value matched an item in the list, else false
16472 selectByValue : function(v, scrollIntoView){
16473 if(v !== undefined && v !== null){
16474 var r = this.findRecord(this.valueField || this.displayField, v);
16476 this.select(this.store.indexOf(r), scrollIntoView);
16484 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16485 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16486 * @param {Number} index The zero-based index of the list item to select
16487 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16488 * selected item if it is not currently in view (defaults to true)
16490 select : function(index, scrollIntoView){
16491 this.selectedIndex = index;
16492 this.view.select(index);
16493 if(scrollIntoView !== false){
16494 var el = this.view.getNode(index);
16496 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16499 this.list.scrollChildIntoView(el, false);
16505 selectNext : function(){
16506 var ct = this.store.getCount();
16508 if(this.selectedIndex == -1){
16510 }else if(this.selectedIndex < ct-1){
16511 this.select(this.selectedIndex+1);
16517 selectPrev : function(){
16518 var ct = this.store.getCount();
16520 if(this.selectedIndex == -1){
16522 }else if(this.selectedIndex != 0){
16523 this.select(this.selectedIndex-1);
16529 onKeyUp : function(e){
16530 if(this.editable !== false && !e.isSpecialKey()){
16531 this.lastKey = e.getKey();
16532 this.dqTask.delay(this.queryDelay);
16537 validateBlur : function(){
16538 return !this.list || !this.list.isVisible();
16542 initQuery : function(){
16544 var v = this.getRawValue();
16546 if(this.tickable && this.editable){
16547 v = this.tickableInputEl().getValue();
16554 doForce : function(){
16555 if(this.inputEl().dom.value.length > 0){
16556 this.inputEl().dom.value =
16557 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16563 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16564 * query allowing the query action to be canceled if needed.
16565 * @param {String} query The SQL query to execute
16566 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16567 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16568 * saved in the current store (defaults to false)
16570 doQuery : function(q, forceAll){
16572 if(q === undefined || q === null){
16577 forceAll: forceAll,
16581 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16586 forceAll = qe.forceAll;
16587 if(forceAll === true || (q.length >= this.minChars)){
16589 this.hasQuery = true;
16591 if(this.lastQuery != q || this.alwaysQuery){
16592 this.lastQuery = q;
16593 if(this.mode == 'local'){
16594 this.selectedIndex = -1;
16596 this.store.clearFilter();
16599 if(this.specialFilter){
16600 this.fireEvent('specialfilter', this);
16605 this.store.filter(this.displayField, q);
16608 this.store.fireEvent("datachanged", this.store);
16615 this.store.baseParams[this.queryParam] = q;
16617 var options = {params : this.getParams(q)};
16620 options.add = true;
16621 options.params.start = this.page * this.pageSize;
16624 this.store.load(options);
16627 * this code will make the page width larger, at the beginning, the list not align correctly,
16628 * we should expand the list on onLoad
16629 * so command out it
16634 this.selectedIndex = -1;
16639 this.loadNext = false;
16643 getParams : function(q){
16645 //p[this.queryParam] = q;
16649 p.limit = this.pageSize;
16655 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16657 collapse : function(){
16658 if(!this.isExpanded()){
16664 this.hasFocus = false;
16668 this.cancelBtn.hide();
16669 this.trigger.show();
16672 this.tickableInputEl().dom.value = '';
16673 this.tickableInputEl().blur();
16678 Roo.get(document).un('mousedown', this.collapseIf, this);
16679 Roo.get(document).un('mousewheel', this.collapseIf, this);
16680 if (!this.editable) {
16681 Roo.get(document).un('keydown', this.listKeyPress, this);
16683 this.fireEvent('collapse', this);
16689 collapseIf : function(e){
16690 var in_combo = e.within(this.el);
16691 var in_list = e.within(this.list);
16692 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16694 if (in_combo || in_list || is_list) {
16695 //e.stopPropagation();
16700 this.onTickableFooterButtonClick(e, false, false);
16708 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16710 expand : function(){
16712 if(this.isExpanded() || !this.hasFocus){
16716 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16717 this.list.setWidth(lw);
16723 this.restrictHeight();
16727 this.tickItems = Roo.apply([], this.item);
16730 this.cancelBtn.show();
16731 this.trigger.hide();
16734 this.tickableInputEl().focus();
16739 Roo.get(document).on('mousedown', this.collapseIf, this);
16740 Roo.get(document).on('mousewheel', this.collapseIf, this);
16741 if (!this.editable) {
16742 Roo.get(document).on('keydown', this.listKeyPress, this);
16745 this.fireEvent('expand', this);
16749 // Implements the default empty TriggerField.onTriggerClick function
16750 onTriggerClick : function(e)
16752 Roo.log('trigger click');
16754 if(this.disabled || !this.triggerList){
16759 this.loadNext = false;
16761 if(this.isExpanded()){
16763 if (!this.blockFocus) {
16764 this.inputEl().focus();
16768 this.hasFocus = true;
16769 if(this.triggerAction == 'all') {
16770 this.doQuery(this.allQuery, true);
16772 this.doQuery(this.getRawValue());
16774 if (!this.blockFocus) {
16775 this.inputEl().focus();
16780 onTickableTriggerClick : function(e)
16787 this.loadNext = false;
16788 this.hasFocus = true;
16790 if(this.triggerAction == 'all') {
16791 this.doQuery(this.allQuery, true);
16793 this.doQuery(this.getRawValue());
16797 onSearchFieldClick : function(e)
16799 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16800 this.onTickableFooterButtonClick(e, false, false);
16804 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16809 this.loadNext = false;
16810 this.hasFocus = true;
16812 if(this.triggerAction == 'all') {
16813 this.doQuery(this.allQuery, true);
16815 this.doQuery(this.getRawValue());
16819 listKeyPress : function(e)
16821 //Roo.log('listkeypress');
16822 // scroll to first matching element based on key pres..
16823 if (e.isSpecialKey()) {
16826 var k = String.fromCharCode(e.getKey()).toUpperCase();
16829 var csel = this.view.getSelectedNodes();
16830 var cselitem = false;
16832 var ix = this.view.indexOf(csel[0]);
16833 cselitem = this.store.getAt(ix);
16834 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16840 this.store.each(function(v) {
16842 // start at existing selection.
16843 if (cselitem.id == v.id) {
16849 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16850 match = this.store.indexOf(v);
16856 if (match === false) {
16857 return true; // no more action?
16860 this.view.select(match);
16861 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16862 sn.scrollIntoView(sn.dom.parentNode, false);
16865 onViewScroll : function(e, t){
16867 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){
16871 this.hasQuery = true;
16873 this.loading = this.list.select('.loading', true).first();
16875 if(this.loading === null){
16876 this.list.createChild({
16878 cls: 'loading roo-select2-more-results roo-select2-active',
16879 html: 'Loading more results...'
16882 this.loading = this.list.select('.loading', true).first();
16884 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16886 this.loading.hide();
16889 this.loading.show();
16894 this.loadNext = true;
16896 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16901 addItem : function(o)
16903 var dv = ''; // display value
16905 if (this.displayField) {
16906 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16908 // this is an error condition!!!
16909 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16916 var choice = this.choices.createChild({
16918 cls: 'roo-select2-search-choice',
16927 cls: 'roo-select2-search-choice-close fa fa-times',
16932 }, this.searchField);
16934 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16936 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16944 this.inputEl().dom.value = '';
16949 onRemoveItem : function(e, _self, o)
16951 e.preventDefault();
16953 this.lastItem = Roo.apply([], this.item);
16955 var index = this.item.indexOf(o.data) * 1;
16958 Roo.log('not this item?!');
16962 this.item.splice(index, 1);
16967 this.fireEvent('remove', this, e);
16973 syncValue : function()
16975 if(!this.item.length){
16982 Roo.each(this.item, function(i){
16983 if(_this.valueField){
16984 value.push(i[_this.valueField]);
16991 this.value = value.join(',');
16993 if(this.hiddenField){
16994 this.hiddenField.dom.value = this.value;
16997 this.store.fireEvent("datachanged", this.store);
17002 clearItem : function()
17004 if(!this.multiple){
17010 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17018 if(this.tickable && !Roo.isTouch){
17019 this.view.refresh();
17023 inputEl: function ()
17025 if(Roo.isIOS && this.useNativeIOS){
17026 return this.el.select('select.roo-ios-select', true).first();
17029 if(Roo.isTouch && this.mobileTouchView){
17030 return this.el.select('input.form-control',true).first();
17034 return this.searchField;
17037 return this.el.select('input.form-control',true).first();
17040 onTickableFooterButtonClick : function(e, btn, el)
17042 e.preventDefault();
17044 this.lastItem = Roo.apply([], this.item);
17046 if(btn && btn.name == 'cancel'){
17047 this.tickItems = Roo.apply([], this.item);
17056 Roo.each(this.tickItems, function(o){
17064 validate : function()
17066 if(this.getVisibilityEl().hasClass('hidden')){
17070 var v = this.getRawValue();
17073 v = this.getValue();
17076 if(this.disabled || this.allowBlank || v.length){
17081 this.markInvalid();
17085 tickableInputEl : function()
17087 if(!this.tickable || !this.editable){
17088 return this.inputEl();
17091 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17095 getAutoCreateTouchView : function()
17100 cls: 'form-group' //input-group
17106 type : this.inputType,
17107 cls : 'form-control x-combo-noedit',
17108 autocomplete: 'new-password',
17109 placeholder : this.placeholder || '',
17114 input.name = this.name;
17118 input.cls += ' input-' + this.size;
17121 if (this.disabled) {
17122 input.disabled = true;
17126 cls : 'roo-combobox-wrap',
17133 inputblock.cls += ' input-group';
17135 inputblock.cn.unshift({
17137 cls : 'input-group-addon input-group-prepend input-group-text',
17142 if(this.removable && !this.multiple){
17143 inputblock.cls += ' roo-removable';
17145 inputblock.cn.push({
17148 cls : 'roo-combo-removable-btn close'
17152 if(this.hasFeedback && !this.allowBlank){
17154 inputblock.cls += ' has-feedback';
17156 inputblock.cn.push({
17158 cls: 'glyphicon form-control-feedback'
17165 inputblock.cls += (this.before) ? '' : ' input-group';
17167 inputblock.cn.push({
17169 cls : 'input-group-addon input-group-append input-group-text',
17175 var ibwrap = inputblock;
17180 cls: 'roo-select2-choices',
17184 cls: 'roo-select2-search-field',
17197 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17202 cls: 'form-hidden-field'
17208 if(!this.multiple && this.showToggleBtn){
17214 if (this.caret != false) {
17217 cls: 'fa fa-' + this.caret
17224 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17226 Roo.bootstrap.version == 3 ? caret : '',
17229 cls: 'combobox-clear',
17243 combobox.cls += ' roo-select2-container-multi';
17246 var align = this.labelAlign || this.parentLabelAlign();
17248 if (align ==='left' && this.fieldLabel.length) {
17253 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17254 tooltip : 'This field is required'
17258 cls : 'control-label col-form-label',
17259 html : this.fieldLabel
17263 cls : 'roo-combobox-wrap ',
17270 var labelCfg = cfg.cn[1];
17271 var contentCfg = cfg.cn[2];
17274 if(this.indicatorpos == 'right'){
17279 cls : 'control-label col-form-label',
17283 html : this.fieldLabel
17287 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17288 tooltip : 'This field is required'
17293 cls : "roo-combobox-wrap ",
17301 labelCfg = cfg.cn[0];
17302 contentCfg = cfg.cn[1];
17307 if(this.labelWidth > 12){
17308 labelCfg.style = "width: " + this.labelWidth + 'px';
17311 if(this.labelWidth < 13 && this.labelmd == 0){
17312 this.labelmd = this.labelWidth;
17315 if(this.labellg > 0){
17316 labelCfg.cls += ' col-lg-' + this.labellg;
17317 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17320 if(this.labelmd > 0){
17321 labelCfg.cls += ' col-md-' + this.labelmd;
17322 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17325 if(this.labelsm > 0){
17326 labelCfg.cls += ' col-sm-' + this.labelsm;
17327 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17330 if(this.labelxs > 0){
17331 labelCfg.cls += ' col-xs-' + this.labelxs;
17332 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17336 } else if ( this.fieldLabel.length) {
17340 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17341 tooltip : 'This field is required'
17345 cls : 'control-label',
17346 html : this.fieldLabel
17357 if(this.indicatorpos == 'right'){
17361 cls : 'control-label',
17362 html : this.fieldLabel,
17366 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17367 tooltip : 'This field is required'
17384 var settings = this;
17386 ['xs','sm','md','lg'].map(function(size){
17387 if (settings[size]) {
17388 cfg.cls += ' col-' + size + '-' + settings[size];
17395 initTouchView : function()
17397 this.renderTouchView();
17399 this.touchViewEl.on('scroll', function(){
17400 this.el.dom.scrollTop = 0;
17403 this.originalValue = this.getValue();
17405 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17407 this.inputEl().on("click", this.showTouchView, this);
17408 if (this.triggerEl) {
17409 this.triggerEl.on("click", this.showTouchView, this);
17413 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17414 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17416 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17418 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17419 this.store.on('load', this.onTouchViewLoad, this);
17420 this.store.on('loadexception', this.onTouchViewLoadException, this);
17422 if(this.hiddenName){
17424 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17426 this.hiddenField.dom.value =
17427 this.hiddenValue !== undefined ? this.hiddenValue :
17428 this.value !== undefined ? this.value : '';
17430 this.el.dom.removeAttribute('name');
17431 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17435 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17436 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17439 if(this.removable && !this.multiple){
17440 var close = this.closeTriggerEl();
17442 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17443 close.on('click', this.removeBtnClick, this, close);
17447 * fix the bug in Safari iOS8
17449 this.inputEl().on("focus", function(e){
17450 document.activeElement.blur();
17453 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17460 renderTouchView : function()
17462 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17463 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17465 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17466 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17468 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17469 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17470 this.touchViewBodyEl.setStyle('overflow', 'auto');
17472 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17473 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17475 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17476 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17480 showTouchView : function()
17486 this.touchViewHeaderEl.hide();
17488 if(this.modalTitle.length){
17489 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17490 this.touchViewHeaderEl.show();
17493 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17494 this.touchViewEl.show();
17496 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17498 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17499 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17501 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17503 if(this.modalTitle.length){
17504 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17507 this.touchViewBodyEl.setHeight(bodyHeight);
17511 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17513 this.touchViewEl.addClass(['in','show']);
17516 if(this._touchViewMask){
17517 Roo.get(document.body).addClass("x-body-masked");
17518 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17519 this._touchViewMask.setStyle('z-index', 10000);
17520 this._touchViewMask.addClass('show');
17523 this.doTouchViewQuery();
17527 hideTouchView : function()
17529 this.touchViewEl.removeClass(['in','show']);
17533 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17535 this.touchViewEl.setStyle('display', 'none');
17538 if(this._touchViewMask){
17539 this._touchViewMask.removeClass('show');
17540 Roo.get(document.body).removeClass("x-body-masked");
17544 setTouchViewValue : function()
17551 Roo.each(this.tickItems, function(o){
17556 this.hideTouchView();
17559 doTouchViewQuery : function()
17568 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17572 if(!this.alwaysQuery || this.mode == 'local'){
17573 this.onTouchViewLoad();
17580 onTouchViewBeforeLoad : function(combo,opts)
17586 onTouchViewLoad : function()
17588 if(this.store.getCount() < 1){
17589 this.onTouchViewEmptyResults();
17593 this.clearTouchView();
17595 var rawValue = this.getRawValue();
17597 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17599 this.tickItems = [];
17601 this.store.data.each(function(d, rowIndex){
17602 var row = this.touchViewListGroup.createChild(template);
17604 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17605 row.addClass(d.data.cls);
17608 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17611 html : d.data[this.displayField]
17614 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17615 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17618 row.removeClass('selected');
17619 if(!this.multiple && this.valueField &&
17620 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17623 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17624 row.addClass('selected');
17627 if(this.multiple && this.valueField &&
17628 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17632 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17633 this.tickItems.push(d.data);
17636 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17640 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17642 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17644 if(this.modalTitle.length){
17645 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17648 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17650 if(this.mobile_restrict_height && listHeight < bodyHeight){
17651 this.touchViewBodyEl.setHeight(listHeight);
17656 if(firstChecked && listHeight > bodyHeight){
17657 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17662 onTouchViewLoadException : function()
17664 this.hideTouchView();
17667 onTouchViewEmptyResults : function()
17669 this.clearTouchView();
17671 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17673 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17677 clearTouchView : function()
17679 this.touchViewListGroup.dom.innerHTML = '';
17682 onTouchViewClick : function(e, el, o)
17684 e.preventDefault();
17687 var rowIndex = o.rowIndex;
17689 var r = this.store.getAt(rowIndex);
17691 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17693 if(!this.multiple){
17694 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17695 c.dom.removeAttribute('checked');
17698 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17700 this.setFromData(r.data);
17702 var close = this.closeTriggerEl();
17708 this.hideTouchView();
17710 this.fireEvent('select', this, r, rowIndex);
17715 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17716 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17717 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17721 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17722 this.addItem(r.data);
17723 this.tickItems.push(r.data);
17727 getAutoCreateNativeIOS : function()
17730 cls: 'form-group' //input-group,
17735 cls : 'roo-ios-select'
17739 combobox.name = this.name;
17742 if (this.disabled) {
17743 combobox.disabled = true;
17746 var settings = this;
17748 ['xs','sm','md','lg'].map(function(size){
17749 if (settings[size]) {
17750 cfg.cls += ' col-' + size + '-' + settings[size];
17760 initIOSView : function()
17762 this.store.on('load', this.onIOSViewLoad, this);
17767 onIOSViewLoad : function()
17769 if(this.store.getCount() < 1){
17773 this.clearIOSView();
17775 if(this.allowBlank) {
17777 var default_text = '-- SELECT --';
17779 if(this.placeholder.length){
17780 default_text = this.placeholder;
17783 if(this.emptyTitle.length){
17784 default_text += ' - ' + this.emptyTitle + ' -';
17787 var opt = this.inputEl().createChild({
17790 html : default_text
17794 o[this.valueField] = 0;
17795 o[this.displayField] = default_text;
17797 this.ios_options.push({
17804 this.store.data.each(function(d, rowIndex){
17808 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17809 html = d.data[this.displayField];
17814 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17815 value = d.data[this.valueField];
17824 if(this.value == d.data[this.valueField]){
17825 option['selected'] = true;
17828 var opt = this.inputEl().createChild(option);
17830 this.ios_options.push({
17837 this.inputEl().on('change', function(){
17838 this.fireEvent('select', this);
17843 clearIOSView: function()
17845 this.inputEl().dom.innerHTML = '';
17847 this.ios_options = [];
17850 setIOSValue: function(v)
17854 if(!this.ios_options){
17858 Roo.each(this.ios_options, function(opts){
17860 opts.el.dom.removeAttribute('selected');
17862 if(opts.data[this.valueField] != v){
17866 opts.el.dom.setAttribute('selected', true);
17872 * @cfg {Boolean} grow
17876 * @cfg {Number} growMin
17880 * @cfg {Number} growMax
17889 Roo.apply(Roo.bootstrap.ComboBox, {
17893 cls: 'modal-header',
17915 cls: 'list-group-item',
17919 cls: 'roo-combobox-list-group-item-value'
17923 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17937 listItemCheckbox : {
17939 cls: 'list-group-item',
17943 cls: 'roo-combobox-list-group-item-value'
17947 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17963 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17968 cls: 'modal-footer',
17976 cls: 'col-xs-6 text-left',
17979 cls: 'btn btn-danger roo-touch-view-cancel',
17985 cls: 'col-xs-6 text-right',
17988 cls: 'btn btn-success roo-touch-view-ok',
17999 Roo.apply(Roo.bootstrap.ComboBox, {
18001 touchViewTemplate : {
18003 cls: 'modal fade roo-combobox-touch-view',
18007 cls: 'modal-dialog',
18008 style : 'position:fixed', // we have to fix position....
18012 cls: 'modal-content',
18014 Roo.bootstrap.ComboBox.header,
18015 Roo.bootstrap.ComboBox.body,
18016 Roo.bootstrap.ComboBox.footer
18025 * Ext JS Library 1.1.1
18026 * Copyright(c) 2006-2007, Ext JS, LLC.
18028 * Originally Released Under LGPL - original licence link has changed is not relivant.
18031 * <script type="text/javascript">
18036 * @extends Roo.util.Observable
18037 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18038 * This class also supports single and multi selection modes. <br>
18039 * Create a data model bound view:
18041 var store = new Roo.data.Store(...);
18043 var view = new Roo.View({
18045 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18047 singleSelect: true,
18048 selectedClass: "ydataview-selected",
18052 // listen for node click?
18053 view.on("click", function(vw, index, node, e){
18054 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18058 dataModel.load("foobar.xml");
18060 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18062 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18063 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18065 * Note: old style constructor is still suported (container, template, config)
18068 * Create a new View
18069 * @param {Object} config The config object
18072 Roo.View = function(config, depreciated_tpl, depreciated_config){
18074 this.parent = false;
18076 if (typeof(depreciated_tpl) == 'undefined') {
18077 // new way.. - universal constructor.
18078 Roo.apply(this, config);
18079 this.el = Roo.get(this.el);
18082 this.el = Roo.get(config);
18083 this.tpl = depreciated_tpl;
18084 Roo.apply(this, depreciated_config);
18086 this.wrapEl = this.el.wrap().wrap();
18087 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18090 if(typeof(this.tpl) == "string"){
18091 this.tpl = new Roo.Template(this.tpl);
18093 // support xtype ctors..
18094 this.tpl = new Roo.factory(this.tpl, Roo);
18098 this.tpl.compile();
18103 * @event beforeclick
18104 * Fires before a click is processed. Returns false to cancel the default action.
18105 * @param {Roo.View} this
18106 * @param {Number} index The index of the target node
18107 * @param {HTMLElement} node The target node
18108 * @param {Roo.EventObject} e The raw event object
18110 "beforeclick" : true,
18113 * Fires when a template node is clicked.
18114 * @param {Roo.View} this
18115 * @param {Number} index The index of the target node
18116 * @param {HTMLElement} node The target node
18117 * @param {Roo.EventObject} e The raw event object
18122 * Fires when a template node is double clicked.
18123 * @param {Roo.View} this
18124 * @param {Number} index The index of the target node
18125 * @param {HTMLElement} node The target node
18126 * @param {Roo.EventObject} e The raw event object
18130 * @event contextmenu
18131 * Fires when a template node is right clicked.
18132 * @param {Roo.View} this
18133 * @param {Number} index The index of the target node
18134 * @param {HTMLElement} node The target node
18135 * @param {Roo.EventObject} e The raw event object
18137 "contextmenu" : true,
18139 * @event selectionchange
18140 * Fires when the selected nodes change.
18141 * @param {Roo.View} this
18142 * @param {Array} selections Array of the selected nodes
18144 "selectionchange" : true,
18147 * @event beforeselect
18148 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18149 * @param {Roo.View} this
18150 * @param {HTMLElement} node The node to be selected
18151 * @param {Array} selections Array of currently selected nodes
18153 "beforeselect" : true,
18155 * @event preparedata
18156 * Fires on every row to render, to allow you to change the data.
18157 * @param {Roo.View} this
18158 * @param {Object} data to be rendered (change this)
18160 "preparedata" : true
18168 "click": this.onClick,
18169 "dblclick": this.onDblClick,
18170 "contextmenu": this.onContextMenu,
18174 this.selections = [];
18176 this.cmp = new Roo.CompositeElementLite([]);
18178 this.store = Roo.factory(this.store, Roo.data);
18179 this.setStore(this.store, true);
18182 if ( this.footer && this.footer.xtype) {
18184 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18186 this.footer.dataSource = this.store;
18187 this.footer.container = fctr;
18188 this.footer = Roo.factory(this.footer, Roo);
18189 fctr.insertFirst(this.el);
18191 // this is a bit insane - as the paging toolbar seems to detach the el..
18192 // dom.parentNode.parentNode.parentNode
18193 // they get detached?
18197 Roo.View.superclass.constructor.call(this);
18202 Roo.extend(Roo.View, Roo.util.Observable, {
18205 * @cfg {Roo.data.Store} store Data store to load data from.
18210 * @cfg {String|Roo.Element} el The container element.
18215 * @cfg {String|Roo.Template} tpl The template used by this View
18219 * @cfg {String} dataName the named area of the template to use as the data area
18220 * Works with domtemplates roo-name="name"
18224 * @cfg {String} selectedClass The css class to add to selected nodes
18226 selectedClass : "x-view-selected",
18228 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18233 * @cfg {String} text to display on mask (default Loading)
18237 * @cfg {Boolean} multiSelect Allow multiple selection
18239 multiSelect : false,
18241 * @cfg {Boolean} singleSelect Allow single selection
18243 singleSelect: false,
18246 * @cfg {Boolean} toggleSelect - selecting
18248 toggleSelect : false,
18251 * @cfg {Boolean} tickable - selecting
18256 * Returns the element this view is bound to.
18257 * @return {Roo.Element}
18259 getEl : function(){
18260 return this.wrapEl;
18266 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18268 refresh : function(){
18269 //Roo.log('refresh');
18272 // if we are using something like 'domtemplate', then
18273 // the what gets used is:
18274 // t.applySubtemplate(NAME, data, wrapping data..)
18275 // the outer template then get' applied with
18276 // the store 'extra data'
18277 // and the body get's added to the
18278 // roo-name="data" node?
18279 // <span class='roo-tpl-{name}'></span> ?????
18283 this.clearSelections();
18284 this.el.update("");
18286 var records = this.store.getRange();
18287 if(records.length < 1) {
18289 // is this valid?? = should it render a template??
18291 this.el.update(this.emptyText);
18295 if (this.dataName) {
18296 this.el.update(t.apply(this.store.meta)); //????
18297 el = this.el.child('.roo-tpl-' + this.dataName);
18300 for(var i = 0, len = records.length; i < len; i++){
18301 var data = this.prepareData(records[i].data, i, records[i]);
18302 this.fireEvent("preparedata", this, data, i, records[i]);
18304 var d = Roo.apply({}, data);
18307 Roo.apply(d, {'roo-id' : Roo.id()});
18311 Roo.each(this.parent.item, function(item){
18312 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18315 Roo.apply(d, {'roo-data-checked' : 'checked'});
18319 html[html.length] = Roo.util.Format.trim(
18321 t.applySubtemplate(this.dataName, d, this.store.meta) :
18328 el.update(html.join(""));
18329 this.nodes = el.dom.childNodes;
18330 this.updateIndexes(0);
18335 * Function to override to reformat the data that is sent to
18336 * the template for each node.
18337 * DEPRICATED - use the preparedata event handler.
18338 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18339 * a JSON object for an UpdateManager bound view).
18341 prepareData : function(data, index, record)
18343 this.fireEvent("preparedata", this, data, index, record);
18347 onUpdate : function(ds, record){
18348 // Roo.log('on update');
18349 this.clearSelections();
18350 var index = this.store.indexOf(record);
18351 var n = this.nodes[index];
18352 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18353 n.parentNode.removeChild(n);
18354 this.updateIndexes(index, index);
18360 onAdd : function(ds, records, index)
18362 //Roo.log(['on Add', ds, records, index] );
18363 this.clearSelections();
18364 if(this.nodes.length == 0){
18368 var n = this.nodes[index];
18369 for(var i = 0, len = records.length; i < len; i++){
18370 var d = this.prepareData(records[i].data, i, records[i]);
18372 this.tpl.insertBefore(n, d);
18375 this.tpl.append(this.el, d);
18378 this.updateIndexes(index);
18381 onRemove : function(ds, record, index){
18382 // Roo.log('onRemove');
18383 this.clearSelections();
18384 var el = this.dataName ?
18385 this.el.child('.roo-tpl-' + this.dataName) :
18388 el.dom.removeChild(this.nodes[index]);
18389 this.updateIndexes(index);
18393 * Refresh an individual node.
18394 * @param {Number} index
18396 refreshNode : function(index){
18397 this.onUpdate(this.store, this.store.getAt(index));
18400 updateIndexes : function(startIndex, endIndex){
18401 var ns = this.nodes;
18402 startIndex = startIndex || 0;
18403 endIndex = endIndex || ns.length - 1;
18404 for(var i = startIndex; i <= endIndex; i++){
18405 ns[i].nodeIndex = i;
18410 * Changes the data store this view uses and refresh the view.
18411 * @param {Store} store
18413 setStore : function(store, initial){
18414 if(!initial && this.store){
18415 this.store.un("datachanged", this.refresh);
18416 this.store.un("add", this.onAdd);
18417 this.store.un("remove", this.onRemove);
18418 this.store.un("update", this.onUpdate);
18419 this.store.un("clear", this.refresh);
18420 this.store.un("beforeload", this.onBeforeLoad);
18421 this.store.un("load", this.onLoad);
18422 this.store.un("loadexception", this.onLoad);
18426 store.on("datachanged", this.refresh, this);
18427 store.on("add", this.onAdd, this);
18428 store.on("remove", this.onRemove, this);
18429 store.on("update", this.onUpdate, this);
18430 store.on("clear", this.refresh, this);
18431 store.on("beforeload", this.onBeforeLoad, this);
18432 store.on("load", this.onLoad, this);
18433 store.on("loadexception", this.onLoad, this);
18441 * onbeforeLoad - masks the loading area.
18444 onBeforeLoad : function(store,opts)
18446 //Roo.log('onBeforeLoad');
18448 this.el.update("");
18450 this.el.mask(this.mask ? this.mask : "Loading" );
18452 onLoad : function ()
18459 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18460 * @param {HTMLElement} node
18461 * @return {HTMLElement} The template node
18463 findItemFromChild : function(node){
18464 var el = this.dataName ?
18465 this.el.child('.roo-tpl-' + this.dataName,true) :
18468 if(!node || node.parentNode == el){
18471 var p = node.parentNode;
18472 while(p && p != el){
18473 if(p.parentNode == el){
18482 onClick : function(e){
18483 var item = this.findItemFromChild(e.getTarget());
18485 var index = this.indexOf(item);
18486 if(this.onItemClick(item, index, e) !== false){
18487 this.fireEvent("click", this, index, item, e);
18490 this.clearSelections();
18495 onContextMenu : function(e){
18496 var item = this.findItemFromChild(e.getTarget());
18498 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18503 onDblClick : function(e){
18504 var item = this.findItemFromChild(e.getTarget());
18506 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18510 onItemClick : function(item, index, e)
18512 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18515 if (this.toggleSelect) {
18516 var m = this.isSelected(item) ? 'unselect' : 'select';
18519 _t[m](item, true, false);
18522 if(this.multiSelect || this.singleSelect){
18523 if(this.multiSelect && e.shiftKey && this.lastSelection){
18524 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18526 this.select(item, this.multiSelect && e.ctrlKey);
18527 this.lastSelection = item;
18530 if(!this.tickable){
18531 e.preventDefault();
18539 * Get the number of selected nodes.
18542 getSelectionCount : function(){
18543 return this.selections.length;
18547 * Get the currently selected nodes.
18548 * @return {Array} An array of HTMLElements
18550 getSelectedNodes : function(){
18551 return this.selections;
18555 * Get the indexes of the selected nodes.
18558 getSelectedIndexes : function(){
18559 var indexes = [], s = this.selections;
18560 for(var i = 0, len = s.length; i < len; i++){
18561 indexes.push(s[i].nodeIndex);
18567 * Clear all selections
18568 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18570 clearSelections : function(suppressEvent){
18571 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18572 this.cmp.elements = this.selections;
18573 this.cmp.removeClass(this.selectedClass);
18574 this.selections = [];
18575 if(!suppressEvent){
18576 this.fireEvent("selectionchange", this, this.selections);
18582 * Returns true if the passed node is selected
18583 * @param {HTMLElement/Number} node The node or node index
18584 * @return {Boolean}
18586 isSelected : function(node){
18587 var s = this.selections;
18591 node = this.getNode(node);
18592 return s.indexOf(node) !== -1;
18597 * @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
18598 * @param {Boolean} keepExisting (optional) true to keep existing selections
18599 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601 select : function(nodeInfo, keepExisting, suppressEvent){
18602 if(nodeInfo instanceof Array){
18604 this.clearSelections(true);
18606 for(var i = 0, len = nodeInfo.length; i < len; i++){
18607 this.select(nodeInfo[i], true, true);
18611 var node = this.getNode(nodeInfo);
18612 if(!node || this.isSelected(node)){
18613 return; // already selected.
18616 this.clearSelections(true);
18619 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18620 Roo.fly(node).addClass(this.selectedClass);
18621 this.selections.push(node);
18622 if(!suppressEvent){
18623 this.fireEvent("selectionchange", this, this.selections);
18631 * @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
18632 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18633 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18635 unselect : function(nodeInfo, keepExisting, suppressEvent)
18637 if(nodeInfo instanceof Array){
18638 Roo.each(this.selections, function(s) {
18639 this.unselect(s, nodeInfo);
18643 var node = this.getNode(nodeInfo);
18644 if(!node || !this.isSelected(node)){
18645 //Roo.log("not selected");
18646 return; // not selected.
18650 Roo.each(this.selections, function(s) {
18652 Roo.fly(node).removeClass(this.selectedClass);
18659 this.selections= ns;
18660 this.fireEvent("selectionchange", this, this.selections);
18664 * Gets a template node.
18665 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18666 * @return {HTMLElement} The node or null if it wasn't found
18668 getNode : function(nodeInfo){
18669 if(typeof nodeInfo == "string"){
18670 return document.getElementById(nodeInfo);
18671 }else if(typeof nodeInfo == "number"){
18672 return this.nodes[nodeInfo];
18678 * Gets a range template nodes.
18679 * @param {Number} startIndex
18680 * @param {Number} endIndex
18681 * @return {Array} An array of nodes
18683 getNodes : function(start, end){
18684 var ns = this.nodes;
18685 start = start || 0;
18686 end = typeof end == "undefined" ? ns.length - 1 : end;
18689 for(var i = start; i <= end; i++){
18693 for(var i = start; i >= end; i--){
18701 * Finds the index of the passed node
18702 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18703 * @return {Number} The index of the node or -1
18705 indexOf : function(node){
18706 node = this.getNode(node);
18707 if(typeof node.nodeIndex == "number"){
18708 return node.nodeIndex;
18710 var ns = this.nodes;
18711 for(var i = 0, len = ns.length; i < len; i++){
18722 * based on jquery fullcalendar
18726 Roo.bootstrap = Roo.bootstrap || {};
18728 * @class Roo.bootstrap.Calendar
18729 * @extends Roo.bootstrap.Component
18730 * Bootstrap Calendar class
18731 * @cfg {Boolean} loadMask (true|false) default false
18732 * @cfg {Object} header generate the user specific header of the calendar, default false
18735 * Create a new Container
18736 * @param {Object} config The config object
18741 Roo.bootstrap.Calendar = function(config){
18742 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18746 * Fires when a date is selected
18747 * @param {DatePicker} this
18748 * @param {Date} date The selected date
18752 * @event monthchange
18753 * Fires when the displayed month changes
18754 * @param {DatePicker} this
18755 * @param {Date} date The selected month
18757 'monthchange': true,
18759 * @event evententer
18760 * Fires when mouse over an event
18761 * @param {Calendar} this
18762 * @param {event} Event
18764 'evententer': true,
18766 * @event eventleave
18767 * Fires when the mouse leaves an
18768 * @param {Calendar} this
18771 'eventleave': true,
18773 * @event eventclick
18774 * Fires when the mouse click an
18775 * @param {Calendar} this
18784 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18787 * @cfg {Number} startDay
18788 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18796 getAutoCreate : function(){
18799 var fc_button = function(name, corner, style, content ) {
18800 return Roo.apply({},{
18802 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18804 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18807 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18818 style : 'width:100%',
18825 cls : 'fc-header-left',
18827 fc_button('prev', 'left', 'arrow', '‹' ),
18828 fc_button('next', 'right', 'arrow', '›' ),
18829 { tag: 'span', cls: 'fc-header-space' },
18830 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18838 cls : 'fc-header-center',
18842 cls: 'fc-header-title',
18845 html : 'month / year'
18853 cls : 'fc-header-right',
18855 /* fc_button('month', 'left', '', 'month' ),
18856 fc_button('week', '', '', 'week' ),
18857 fc_button('day', 'right', '', 'day' )
18869 header = this.header;
18872 var cal_heads = function() {
18874 // fixme - handle this.
18876 for (var i =0; i < Date.dayNames.length; i++) {
18877 var d = Date.dayNames[i];
18880 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18881 html : d.substring(0,3)
18885 ret[0].cls += ' fc-first';
18886 ret[6].cls += ' fc-last';
18889 var cal_cell = function(n) {
18892 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18897 cls: 'fc-day-number',
18901 cls: 'fc-day-content',
18905 style: 'position: relative;' // height: 17px;
18917 var cal_rows = function() {
18920 for (var r = 0; r < 6; r++) {
18927 for (var i =0; i < Date.dayNames.length; i++) {
18928 var d = Date.dayNames[i];
18929 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18932 row.cn[0].cls+=' fc-first';
18933 row.cn[0].cn[0].style = 'min-height:90px';
18934 row.cn[6].cls+=' fc-last';
18938 ret[0].cls += ' fc-first';
18939 ret[4].cls += ' fc-prev-last';
18940 ret[5].cls += ' fc-last';
18947 cls: 'fc-border-separate',
18948 style : 'width:100%',
18956 cls : 'fc-first fc-last',
18974 cls : 'fc-content',
18975 style : "position: relative;",
18978 cls : 'fc-view fc-view-month fc-grid',
18979 style : 'position: relative',
18980 unselectable : 'on',
18983 cls : 'fc-event-container',
18984 style : 'position:absolute;z-index:8;top:0;left:0;'
19002 initEvents : function()
19005 throw "can not find store for calendar";
19011 style: "text-align:center",
19015 style: "background-color:white;width:50%;margin:250 auto",
19019 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19030 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19032 var size = this.el.select('.fc-content', true).first().getSize();
19033 this.maskEl.setSize(size.width, size.height);
19034 this.maskEl.enableDisplayMode("block");
19035 if(!this.loadMask){
19036 this.maskEl.hide();
19039 this.store = Roo.factory(this.store, Roo.data);
19040 this.store.on('load', this.onLoad, this);
19041 this.store.on('beforeload', this.onBeforeLoad, this);
19045 this.cells = this.el.select('.fc-day',true);
19046 //Roo.log(this.cells);
19047 this.textNodes = this.el.query('.fc-day-number');
19048 this.cells.addClassOnOver('fc-state-hover');
19050 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19051 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19052 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19053 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19055 this.on('monthchange', this.onMonthChange, this);
19057 this.update(new Date().clearTime());
19060 resize : function() {
19061 var sz = this.el.getSize();
19063 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19064 this.el.select('.fc-day-content div',true).setHeight(34);
19069 showPrevMonth : function(e){
19070 this.update(this.activeDate.add("mo", -1));
19072 showToday : function(e){
19073 this.update(new Date().clearTime());
19076 showNextMonth : function(e){
19077 this.update(this.activeDate.add("mo", 1));
19081 showPrevYear : function(){
19082 this.update(this.activeDate.add("y", -1));
19086 showNextYear : function(){
19087 this.update(this.activeDate.add("y", 1));
19092 update : function(date)
19094 var vd = this.activeDate;
19095 this.activeDate = date;
19096 // if(vd && this.el){
19097 // var t = date.getTime();
19098 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19099 // Roo.log('using add remove');
19101 // this.fireEvent('monthchange', this, date);
19103 // this.cells.removeClass("fc-state-highlight");
19104 // this.cells.each(function(c){
19105 // if(c.dateValue == t){
19106 // c.addClass("fc-state-highlight");
19107 // setTimeout(function(){
19108 // try{c.dom.firstChild.focus();}catch(e){}
19118 var days = date.getDaysInMonth();
19120 var firstOfMonth = date.getFirstDateOfMonth();
19121 var startingPos = firstOfMonth.getDay()-this.startDay;
19123 if(startingPos < this.startDay){
19127 var pm = date.add(Date.MONTH, -1);
19128 var prevStart = pm.getDaysInMonth()-startingPos;
19130 this.cells = this.el.select('.fc-day',true);
19131 this.textNodes = this.el.query('.fc-day-number');
19132 this.cells.addClassOnOver('fc-state-hover');
19134 var cells = this.cells.elements;
19135 var textEls = this.textNodes;
19137 Roo.each(cells, function(cell){
19138 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19141 days += startingPos;
19143 // convert everything to numbers so it's fast
19144 var day = 86400000;
19145 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19148 //Roo.log(prevStart);
19150 var today = new Date().clearTime().getTime();
19151 var sel = date.clearTime().getTime();
19152 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19153 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19154 var ddMatch = this.disabledDatesRE;
19155 var ddText = this.disabledDatesText;
19156 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19157 var ddaysText = this.disabledDaysText;
19158 var format = this.format;
19160 var setCellClass = function(cal, cell){
19164 //Roo.log('set Cell Class');
19166 var t = d.getTime();
19170 cell.dateValue = t;
19172 cell.className += " fc-today";
19173 cell.className += " fc-state-highlight";
19174 cell.title = cal.todayText;
19177 // disable highlight in other month..
19178 //cell.className += " fc-state-highlight";
19183 cell.className = " fc-state-disabled";
19184 cell.title = cal.minText;
19188 cell.className = " fc-state-disabled";
19189 cell.title = cal.maxText;
19193 if(ddays.indexOf(d.getDay()) != -1){
19194 cell.title = ddaysText;
19195 cell.className = " fc-state-disabled";
19198 if(ddMatch && format){
19199 var fvalue = d.dateFormat(format);
19200 if(ddMatch.test(fvalue)){
19201 cell.title = ddText.replace("%0", fvalue);
19202 cell.className = " fc-state-disabled";
19206 if (!cell.initialClassName) {
19207 cell.initialClassName = cell.dom.className;
19210 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19215 for(; i < startingPos; i++) {
19216 textEls[i].innerHTML = (++prevStart);
19217 d.setDate(d.getDate()+1);
19219 cells[i].className = "fc-past fc-other-month";
19220 setCellClass(this, cells[i]);
19225 for(; i < days; i++){
19226 intDay = i - startingPos + 1;
19227 textEls[i].innerHTML = (intDay);
19228 d.setDate(d.getDate()+1);
19230 cells[i].className = ''; // "x-date-active";
19231 setCellClass(this, cells[i]);
19235 for(; i < 42; i++) {
19236 textEls[i].innerHTML = (++extraDays);
19237 d.setDate(d.getDate()+1);
19239 cells[i].className = "fc-future fc-other-month";
19240 setCellClass(this, cells[i]);
19243 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19245 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19247 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19248 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19250 if(totalRows != 6){
19251 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19252 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19255 this.fireEvent('monthchange', this, date);
19259 if(!this.internalRender){
19260 var main = this.el.dom.firstChild;
19261 var w = main.offsetWidth;
19262 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19263 Roo.fly(main).setWidth(w);
19264 this.internalRender = true;
19265 // opera does not respect the auto grow header center column
19266 // then, after it gets a width opera refuses to recalculate
19267 // without a second pass
19268 if(Roo.isOpera && !this.secondPass){
19269 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19270 this.secondPass = true;
19271 this.update.defer(10, this, [date]);
19278 findCell : function(dt) {
19279 dt = dt.clearTime().getTime();
19281 this.cells.each(function(c){
19282 //Roo.log("check " +c.dateValue + '?=' + dt);
19283 if(c.dateValue == dt){
19293 findCells : function(ev) {
19294 var s = ev.start.clone().clearTime().getTime();
19296 var e= ev.end.clone().clearTime().getTime();
19299 this.cells.each(function(c){
19300 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19302 if(c.dateValue > e){
19305 if(c.dateValue < s){
19314 // findBestRow: function(cells)
19318 // for (var i =0 ; i < cells.length;i++) {
19319 // ret = Math.max(cells[i].rows || 0,ret);
19326 addItem : function(ev)
19328 // look for vertical location slot in
19329 var cells = this.findCells(ev);
19331 // ev.row = this.findBestRow(cells);
19333 // work out the location.
19337 for(var i =0; i < cells.length; i++) {
19339 cells[i].row = cells[0].row;
19342 cells[i].row = cells[i].row + 1;
19352 if (crow.start.getY() == cells[i].getY()) {
19354 crow.end = cells[i];
19371 cells[0].events.push(ev);
19373 this.calevents.push(ev);
19376 clearEvents: function() {
19378 if(!this.calevents){
19382 Roo.each(this.cells.elements, function(c){
19388 Roo.each(this.calevents, function(e) {
19389 Roo.each(e.els, function(el) {
19390 el.un('mouseenter' ,this.onEventEnter, this);
19391 el.un('mouseleave' ,this.onEventLeave, this);
19396 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19402 renderEvents: function()
19406 this.cells.each(function(c) {
19415 if(c.row != c.events.length){
19416 r = 4 - (4 - (c.row - c.events.length));
19419 c.events = ev.slice(0, r);
19420 c.more = ev.slice(r);
19422 if(c.more.length && c.more.length == 1){
19423 c.events.push(c.more.pop());
19426 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19430 this.cells.each(function(c) {
19432 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19435 for (var e = 0; e < c.events.length; e++){
19436 var ev = c.events[e];
19437 var rows = ev.rows;
19439 for(var i = 0; i < rows.length; i++) {
19441 // how many rows should it span..
19444 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19445 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19447 unselectable : "on",
19450 cls: 'fc-event-inner',
19454 // cls: 'fc-event-time',
19455 // html : cells.length > 1 ? '' : ev.time
19459 cls: 'fc-event-title',
19460 html : String.format('{0}', ev.title)
19467 cls: 'ui-resizable-handle ui-resizable-e',
19468 html : '  '
19475 cfg.cls += ' fc-event-start';
19477 if ((i+1) == rows.length) {
19478 cfg.cls += ' fc-event-end';
19481 var ctr = _this.el.select('.fc-event-container',true).first();
19482 var cg = ctr.createChild(cfg);
19484 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19485 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19487 var r = (c.more.length) ? 1 : 0;
19488 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19489 cg.setWidth(ebox.right - sbox.x -2);
19491 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19492 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19493 cg.on('click', _this.onEventClick, _this, ev);
19504 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19505 style : 'position: absolute',
19506 unselectable : "on",
19509 cls: 'fc-event-inner',
19513 cls: 'fc-event-title',
19521 cls: 'ui-resizable-handle ui-resizable-e',
19522 html : '  '
19528 var ctr = _this.el.select('.fc-event-container',true).first();
19529 var cg = ctr.createChild(cfg);
19531 var sbox = c.select('.fc-day-content',true).first().getBox();
19532 var ebox = c.select('.fc-day-content',true).first().getBox();
19534 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19535 cg.setWidth(ebox.right - sbox.x -2);
19537 cg.on('click', _this.onMoreEventClick, _this, c.more);
19547 onEventEnter: function (e, el,event,d) {
19548 this.fireEvent('evententer', this, el, event);
19551 onEventLeave: function (e, el,event,d) {
19552 this.fireEvent('eventleave', this, el, event);
19555 onEventClick: function (e, el,event,d) {
19556 this.fireEvent('eventclick', this, el, event);
19559 onMonthChange: function () {
19563 onMoreEventClick: function(e, el, more)
19567 this.calpopover.placement = 'right';
19568 this.calpopover.setTitle('More');
19570 this.calpopover.setContent('');
19572 var ctr = this.calpopover.el.select('.popover-content', true).first();
19574 Roo.each(more, function(m){
19576 cls : 'fc-event-hori fc-event-draggable',
19579 var cg = ctr.createChild(cfg);
19581 cg.on('click', _this.onEventClick, _this, m);
19584 this.calpopover.show(el);
19589 onLoad: function ()
19591 this.calevents = [];
19594 if(this.store.getCount() > 0){
19595 this.store.data.each(function(d){
19598 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19599 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19600 time : d.data.start_time,
19601 title : d.data.title,
19602 description : d.data.description,
19603 venue : d.data.venue
19608 this.renderEvents();
19610 if(this.calevents.length && this.loadMask){
19611 this.maskEl.hide();
19615 onBeforeLoad: function()
19617 this.clearEvents();
19619 this.maskEl.show();
19633 * @class Roo.bootstrap.Popover
19634 * @extends Roo.bootstrap.Component
19635 * Bootstrap Popover class
19636 * @cfg {String} html contents of the popover (or false to use children..)
19637 * @cfg {String} title of popover (or false to hide)
19638 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19639 * @cfg {String} trigger click || hover (or false to trigger manually)
19640 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19641 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19642 * - if false and it has a 'parent' then it will be automatically added to that element
19643 * - if string - Roo.get will be called
19644 * @cfg {Number} delay - delay before showing
19647 * Create a new Popover
19648 * @param {Object} config The config object
19651 Roo.bootstrap.Popover = function(config){
19652 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19658 * After the popover show
19660 * @param {Roo.bootstrap.Popover} this
19665 * After the popover hide
19667 * @param {Roo.bootstrap.Popover} this
19673 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19678 placement : 'right',
19679 trigger : 'hover', // hover
19685 can_build_overlaid : false,
19687 maskEl : false, // the mask element
19690 alignEl : false, // when show is called with an element - this get's stored.
19692 getChildContainer : function()
19694 return this.contentEl;
19697 getPopoverHeader : function()
19699 this.title = true; // flag not to hide it..
19700 this.headerEl.addClass('p-0');
19701 return this.headerEl
19705 getAutoCreate : function(){
19708 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19709 style: 'display:block',
19715 cls : 'popover-inner ',
19719 cls: 'popover-title popover-header',
19720 html : this.title === false ? '' : this.title
19723 cls : 'popover-content popover-body ' + (this.cls || ''),
19724 html : this.html || ''
19735 * @param {string} the title
19737 setTitle: function(str)
19741 this.headerEl.dom.innerHTML = str;
19746 * @param {string} the body content
19748 setContent: function(str)
19751 if (this.contentEl) {
19752 this.contentEl.dom.innerHTML = str;
19756 // as it get's added to the bottom of the page.
19757 onRender : function(ct, position)
19759 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19764 var cfg = Roo.apply({}, this.getAutoCreate());
19768 cfg.cls += ' ' + this.cls;
19771 cfg.style = this.style;
19773 //Roo.log("adding to ");
19774 this.el = Roo.get(document.body).createChild(cfg, position);
19775 // Roo.log(this.el);
19778 this.contentEl = this.el.select('.popover-content',true).first();
19779 this.headerEl = this.el.select('.popover-title',true).first();
19782 if(typeof(this.items) != 'undefined'){
19783 var items = this.items;
19786 for(var i =0;i < items.length;i++) {
19787 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19791 this.items = nitems;
19793 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19794 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19801 resizeMask : function()
19803 this.maskEl.setSize(
19804 Roo.lib.Dom.getViewWidth(true),
19805 Roo.lib.Dom.getViewHeight(true)
19809 initEvents : function()
19813 Roo.bootstrap.Popover.register(this);
19816 this.arrowEl = this.el.select('.arrow',true).first();
19817 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19818 this.el.enableDisplayMode('block');
19822 if (this.over === false && !this.parent()) {
19825 if (this.triggers === false) {
19830 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19831 var triggers = this.trigger ? this.trigger.split(' ') : [];
19832 Roo.each(triggers, function(trigger) {
19834 if (trigger == 'click') {
19835 on_el.on('click', this.toggle, this);
19836 } else if (trigger != 'manual') {
19837 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19838 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19840 on_el.on(eventIn ,this.enter, this);
19841 on_el.on(eventOut, this.leave, this);
19851 toggle : function () {
19852 this.hoverState == 'in' ? this.leave() : this.enter();
19855 enter : function () {
19857 clearTimeout(this.timeout);
19859 this.hoverState = 'in';
19861 if (!this.delay || !this.delay.show) {
19866 this.timeout = setTimeout(function () {
19867 if (_t.hoverState == 'in') {
19870 }, this.delay.show)
19873 leave : function() {
19874 clearTimeout(this.timeout);
19876 this.hoverState = 'out';
19878 if (!this.delay || !this.delay.hide) {
19883 this.timeout = setTimeout(function () {
19884 if (_t.hoverState == 'out') {
19887 }, this.delay.hide)
19891 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19892 * @param {string} (left|right|top|bottom) position
19894 show : function (on_el, placement)
19896 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19897 on_el = on_el || false; // default to false
19900 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19901 on_el = this.parent().el;
19902 } else if (this.over) {
19903 Roo.get(this.over);
19908 this.alignEl = Roo.get( on_el );
19911 this.render(document.body);
19917 if (this.title === false) {
19918 this.headerEl.hide();
19923 this.el.dom.style.display = 'block';
19926 if (this.alignEl) {
19927 this.updatePosition(this.placement, true);
19930 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19931 var es = this.el.getSize();
19932 var x = Roo.lib.Dom.getViewWidth()/2;
19933 var y = Roo.lib.Dom.getViewHeight()/2;
19934 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19939 //var arrow = this.el.select('.arrow',true).first();
19940 //arrow.set(align[2],
19942 this.el.addClass('in');
19946 this.hoverState = 'in';
19949 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19950 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19951 this.maskEl.dom.style.display = 'block';
19952 this.maskEl.addClass('show');
19954 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19956 this.fireEvent('show', this);
19960 * fire this manually after loading a grid in the table for example
19961 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19962 * @param {Boolean} try and move it if we cant get right position.
19964 updatePosition : function(placement, try_move)
19966 // allow for calling with no parameters
19967 placement = placement ? placement : this.placement;
19968 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19970 this.el.removeClass([
19971 'fade','top','bottom', 'left', 'right','in',
19972 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19974 this.el.addClass(placement + ' bs-popover-' + placement);
19976 if (!this.alignEl ) {
19980 switch (placement) {
19982 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19983 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19984 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19985 //normal display... or moved up/down.
19986 this.el.setXY(offset);
19987 var xy = this.alignEl.getAnchorXY('tr', false);
19989 this.arrowEl.setXY(xy);
19992 // continue through...
19993 return this.updatePosition('left', false);
19997 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19998 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19999 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20000 //normal display... or moved up/down.
20001 this.el.setXY(offset);
20002 var xy = this.alignEl.getAnchorXY('tl', false);
20003 xy[0]-=10;xy[1]+=5; // << fix me
20004 this.arrowEl.setXY(xy);
20008 return this.updatePosition('right', false);
20011 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20012 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20013 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20014 //normal display... or moved up/down.
20015 this.el.setXY(offset);
20016 var xy = this.alignEl.getAnchorXY('t', false);
20017 xy[1]-=10; // << fix me
20018 this.arrowEl.setXY(xy);
20022 return this.updatePosition('bottom', false);
20025 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20026 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20027 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20028 //normal display... or moved up/down.
20029 this.el.setXY(offset);
20030 var xy = this.alignEl.getAnchorXY('b', false);
20031 xy[1]+=2; // << fix me
20032 this.arrowEl.setXY(xy);
20036 return this.updatePosition('top', false);
20047 this.el.setXY([0,0]);
20048 this.el.removeClass('in');
20050 this.hoverState = null;
20051 this.maskEl.hide(); // always..
20052 this.fireEvent('hide', this);
20058 Roo.apply(Roo.bootstrap.Popover, {
20061 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20062 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20063 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20064 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20069 clickHander : false,
20072 onMouseDown : function(e)
20074 if (!e.getTarget(".roo-popover")) {
20082 register : function(popup)
20084 if (!Roo.bootstrap.Popover.clickHandler) {
20085 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20087 // hide other popups.
20089 this.popups.push(popup);
20091 hideAll : function()
20093 this.popups.forEach(function(p) {
20101 * Card header - holder for the card header elements.
20106 * @class Roo.bootstrap.PopoverNav
20107 * @extends Roo.bootstrap.NavGroup
20108 * Bootstrap Popover header navigation class
20110 * Create a new Popover Header Navigation
20111 * @param {Object} config The config object
20114 Roo.bootstrap.PopoverNav = function(config){
20115 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20118 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20121 container_method : 'getPopoverHeader'
20139 * @class Roo.bootstrap.Progress
20140 * @extends Roo.bootstrap.Component
20141 * Bootstrap Progress class
20142 * @cfg {Boolean} striped striped of the progress bar
20143 * @cfg {Boolean} active animated of the progress bar
20147 * Create a new Progress
20148 * @param {Object} config The config object
20151 Roo.bootstrap.Progress = function(config){
20152 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20155 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20160 getAutoCreate : function(){
20168 cfg.cls += ' progress-striped';
20172 cfg.cls += ' active';
20191 * @class Roo.bootstrap.ProgressBar
20192 * @extends Roo.bootstrap.Component
20193 * Bootstrap ProgressBar class
20194 * @cfg {Number} aria_valuenow aria-value now
20195 * @cfg {Number} aria_valuemin aria-value min
20196 * @cfg {Number} aria_valuemax aria-value max
20197 * @cfg {String} label label for the progress bar
20198 * @cfg {String} panel (success | info | warning | danger )
20199 * @cfg {String} role role of the progress bar
20200 * @cfg {String} sr_only text
20204 * Create a new ProgressBar
20205 * @param {Object} config The config object
20208 Roo.bootstrap.ProgressBar = function(config){
20209 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20212 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20216 aria_valuemax : 100,
20222 getAutoCreate : function()
20227 cls: 'progress-bar',
20228 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20240 cfg.role = this.role;
20243 if(this.aria_valuenow){
20244 cfg['aria-valuenow'] = this.aria_valuenow;
20247 if(this.aria_valuemin){
20248 cfg['aria-valuemin'] = this.aria_valuemin;
20251 if(this.aria_valuemax){
20252 cfg['aria-valuemax'] = this.aria_valuemax;
20255 if(this.label && !this.sr_only){
20256 cfg.html = this.label;
20260 cfg.cls += ' progress-bar-' + this.panel;
20266 update : function(aria_valuenow)
20268 this.aria_valuenow = aria_valuenow;
20270 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20285 * @class Roo.bootstrap.TabGroup
20286 * @extends Roo.bootstrap.Column
20287 * Bootstrap Column class
20288 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20289 * @cfg {Boolean} carousel true to make the group behave like a carousel
20290 * @cfg {Boolean} bullets show bullets for the panels
20291 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20292 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20293 * @cfg {Boolean} showarrow (true|false) show arrow default true
20296 * Create a new TabGroup
20297 * @param {Object} config The config object
20300 Roo.bootstrap.TabGroup = function(config){
20301 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20303 this.navId = Roo.id();
20306 Roo.bootstrap.TabGroup.register(this);
20310 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20313 transition : false,
20318 slideOnTouch : false,
20321 getAutoCreate : function()
20323 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20325 cfg.cls += ' tab-content';
20327 if (this.carousel) {
20328 cfg.cls += ' carousel slide';
20331 cls : 'carousel-inner',
20335 if(this.bullets && !Roo.isTouch){
20338 cls : 'carousel-bullets',
20342 if(this.bullets_cls){
20343 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20350 cfg.cn[0].cn.push(bullets);
20353 if(this.showarrow){
20354 cfg.cn[0].cn.push({
20356 class : 'carousel-arrow',
20360 class : 'carousel-prev',
20364 class : 'fa fa-chevron-left'
20370 class : 'carousel-next',
20374 class : 'fa fa-chevron-right'
20387 initEvents: function()
20389 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20390 // this.el.on("touchstart", this.onTouchStart, this);
20393 if(this.autoslide){
20396 this.slideFn = window.setInterval(function() {
20397 _this.showPanelNext();
20401 if(this.showarrow){
20402 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20403 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20409 // onTouchStart : function(e, el, o)
20411 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20415 // this.showPanelNext();
20419 getChildContainer : function()
20421 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20425 * register a Navigation item
20426 * @param {Roo.bootstrap.NavItem} the navitem to add
20428 register : function(item)
20430 this.tabs.push( item);
20431 item.navId = this.navId; // not really needed..
20436 getActivePanel : function()
20439 Roo.each(this.tabs, function(t) {
20449 getPanelByName : function(n)
20452 Roo.each(this.tabs, function(t) {
20453 if (t.tabId == n) {
20461 indexOfPanel : function(p)
20464 Roo.each(this.tabs, function(t,i) {
20465 if (t.tabId == p.tabId) {
20474 * show a specific panel
20475 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20476 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20478 showPanel : function (pan)
20480 if(this.transition || typeof(pan) == 'undefined'){
20481 Roo.log("waiting for the transitionend");
20485 if (typeof(pan) == 'number') {
20486 pan = this.tabs[pan];
20489 if (typeof(pan) == 'string') {
20490 pan = this.getPanelByName(pan);
20493 var cur = this.getActivePanel();
20496 Roo.log('pan or acitve pan is undefined');
20500 if (pan.tabId == this.getActivePanel().tabId) {
20504 if (false === cur.fireEvent('beforedeactivate')) {
20508 if(this.bullets > 0 && !Roo.isTouch){
20509 this.setActiveBullet(this.indexOfPanel(pan));
20512 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20514 //class="carousel-item carousel-item-next carousel-item-left"
20516 this.transition = true;
20517 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20518 var lr = dir == 'next' ? 'left' : 'right';
20519 pan.el.addClass(dir); // or prev
20520 pan.el.addClass('carousel-item-' + dir); // or prev
20521 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20522 cur.el.addClass(lr); // or right
20523 pan.el.addClass(lr);
20524 cur.el.addClass('carousel-item-' +lr); // or right
20525 pan.el.addClass('carousel-item-' +lr);
20529 cur.el.on('transitionend', function() {
20530 Roo.log("trans end?");
20532 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20533 pan.setActive(true);
20535 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20536 cur.setActive(false);
20538 _this.transition = false;
20540 }, this, { single: true } );
20545 cur.setActive(false);
20546 pan.setActive(true);
20551 showPanelNext : function()
20553 var i = this.indexOfPanel(this.getActivePanel());
20555 if (i >= this.tabs.length - 1 && !this.autoslide) {
20559 if (i >= this.tabs.length - 1 && this.autoslide) {
20563 this.showPanel(this.tabs[i+1]);
20566 showPanelPrev : function()
20568 var i = this.indexOfPanel(this.getActivePanel());
20570 if (i < 1 && !this.autoslide) {
20574 if (i < 1 && this.autoslide) {
20575 i = this.tabs.length;
20578 this.showPanel(this.tabs[i-1]);
20582 addBullet: function()
20584 if(!this.bullets || Roo.isTouch){
20587 var ctr = this.el.select('.carousel-bullets',true).first();
20588 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20589 var bullet = ctr.createChild({
20590 cls : 'bullet bullet-' + i
20591 },ctr.dom.lastChild);
20596 bullet.on('click', (function(e, el, o, ii, t){
20598 e.preventDefault();
20600 this.showPanel(ii);
20602 if(this.autoslide && this.slideFn){
20603 clearInterval(this.slideFn);
20604 this.slideFn = window.setInterval(function() {
20605 _this.showPanelNext();
20609 }).createDelegate(this, [i, bullet], true));
20614 setActiveBullet : function(i)
20620 Roo.each(this.el.select('.bullet', true).elements, function(el){
20621 el.removeClass('selected');
20624 var bullet = this.el.select('.bullet-' + i, true).first();
20630 bullet.addClass('selected');
20641 Roo.apply(Roo.bootstrap.TabGroup, {
20645 * register a Navigation Group
20646 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20648 register : function(navgrp)
20650 this.groups[navgrp.navId] = navgrp;
20654 * fetch a Navigation Group based on the navigation ID
20655 * if one does not exist , it will get created.
20656 * @param {string} the navgroup to add
20657 * @returns {Roo.bootstrap.NavGroup} the navgroup
20659 get: function(navId) {
20660 if (typeof(this.groups[navId]) == 'undefined') {
20661 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20663 return this.groups[navId] ;
20678 * @class Roo.bootstrap.TabPanel
20679 * @extends Roo.bootstrap.Component
20680 * Bootstrap TabPanel class
20681 * @cfg {Boolean} active panel active
20682 * @cfg {String} html panel content
20683 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20684 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20685 * @cfg {String} href click to link..
20686 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20690 * Create a new TabPanel
20691 * @param {Object} config The config object
20694 Roo.bootstrap.TabPanel = function(config){
20695 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20699 * Fires when the active status changes
20700 * @param {Roo.bootstrap.TabPanel} this
20701 * @param {Boolean} state the new state
20706 * @event beforedeactivate
20707 * Fires before a tab is de-activated - can be used to do validation on a form.
20708 * @param {Roo.bootstrap.TabPanel} this
20709 * @return {Boolean} false if there is an error
20712 'beforedeactivate': true
20715 this.tabId = this.tabId || Roo.id();
20719 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20726 touchSlide : false,
20727 getAutoCreate : function(){
20732 // item is needed for carousel - not sure if it has any effect otherwise
20733 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20734 html: this.html || ''
20738 cfg.cls += ' active';
20742 cfg.tabId = this.tabId;
20750 initEvents: function()
20752 var p = this.parent();
20754 this.navId = this.navId || p.navId;
20756 if (typeof(this.navId) != 'undefined') {
20757 // not really needed.. but just in case.. parent should be a NavGroup.
20758 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20762 var i = tg.tabs.length - 1;
20764 if(this.active && tg.bullets > 0 && i < tg.bullets){
20765 tg.setActiveBullet(i);
20769 this.el.on('click', this.onClick, this);
20771 if(Roo.isTouch && this.touchSlide){
20772 this.el.on("touchstart", this.onTouchStart, this);
20773 this.el.on("touchmove", this.onTouchMove, this);
20774 this.el.on("touchend", this.onTouchEnd, this);
20779 onRender : function(ct, position)
20781 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20784 setActive : function(state)
20786 Roo.log("panel - set active " + this.tabId + "=" + state);
20788 this.active = state;
20790 this.el.removeClass('active');
20792 } else if (!this.el.hasClass('active')) {
20793 this.el.addClass('active');
20796 this.fireEvent('changed', this, state);
20799 onClick : function(e)
20801 e.preventDefault();
20803 if(!this.href.length){
20807 window.location.href = this.href;
20816 onTouchStart : function(e)
20818 this.swiping = false;
20820 this.startX = e.browserEvent.touches[0].clientX;
20821 this.startY = e.browserEvent.touches[0].clientY;
20824 onTouchMove : function(e)
20826 this.swiping = true;
20828 this.endX = e.browserEvent.touches[0].clientX;
20829 this.endY = e.browserEvent.touches[0].clientY;
20832 onTouchEnd : function(e)
20839 var tabGroup = this.parent();
20841 if(this.endX > this.startX){ // swiping right
20842 tabGroup.showPanelPrev();
20846 if(this.startX > this.endX){ // swiping left
20847 tabGroup.showPanelNext();
20866 * @class Roo.bootstrap.DateField
20867 * @extends Roo.bootstrap.Input
20868 * Bootstrap DateField class
20869 * @cfg {Number} weekStart default 0
20870 * @cfg {String} viewMode default empty, (months|years)
20871 * @cfg {String} minViewMode default empty, (months|years)
20872 * @cfg {Number} startDate default -Infinity
20873 * @cfg {Number} endDate default Infinity
20874 * @cfg {Boolean} todayHighlight default false
20875 * @cfg {Boolean} todayBtn default false
20876 * @cfg {Boolean} calendarWeeks default false
20877 * @cfg {Object} daysOfWeekDisabled default empty
20878 * @cfg {Boolean} singleMode default false (true | false)
20880 * @cfg {Boolean} keyboardNavigation default true
20881 * @cfg {String} language default en
20884 * Create a new DateField
20885 * @param {Object} config The config object
20888 Roo.bootstrap.DateField = function(config){
20889 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20893 * Fires when this field show.
20894 * @param {Roo.bootstrap.DateField} this
20895 * @param {Mixed} date The date value
20900 * Fires when this field hide.
20901 * @param {Roo.bootstrap.DateField} this
20902 * @param {Mixed} date The date value
20907 * Fires when select a date.
20908 * @param {Roo.bootstrap.DateField} this
20909 * @param {Mixed} date The date value
20913 * @event beforeselect
20914 * Fires when before select a date.
20915 * @param {Roo.bootstrap.DateField} this
20916 * @param {Mixed} date The date value
20918 beforeselect : true
20922 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20925 * @cfg {String} format
20926 * The default date format string which can be overriden for localization support. The format must be
20927 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20931 * @cfg {String} altFormats
20932 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20933 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20935 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20943 todayHighlight : false,
20949 keyboardNavigation: true,
20951 calendarWeeks: false,
20953 startDate: -Infinity,
20957 daysOfWeekDisabled: [],
20961 singleMode : false,
20963 UTCDate: function()
20965 return new Date(Date.UTC.apply(Date, arguments));
20968 UTCToday: function()
20970 var today = new Date();
20971 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20974 getDate: function() {
20975 var d = this.getUTCDate();
20976 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20979 getUTCDate: function() {
20983 setDate: function(d) {
20984 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20987 setUTCDate: function(d) {
20989 this.setValue(this.formatDate(this.date));
20992 onRender: function(ct, position)
20995 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20997 this.language = this.language || 'en';
20998 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20999 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21001 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21002 this.format = this.format || 'm/d/y';
21003 this.isInline = false;
21004 this.isInput = true;
21005 this.component = this.el.select('.add-on', true).first() || false;
21006 this.component = (this.component && this.component.length === 0) ? false : this.component;
21007 this.hasInput = this.component && this.inputEl().length;
21009 if (typeof(this.minViewMode === 'string')) {
21010 switch (this.minViewMode) {
21012 this.minViewMode = 1;
21015 this.minViewMode = 2;
21018 this.minViewMode = 0;
21023 if (typeof(this.viewMode === 'string')) {
21024 switch (this.viewMode) {
21037 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21039 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21041 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21043 this.picker().on('mousedown', this.onMousedown, this);
21044 this.picker().on('click', this.onClick, this);
21046 this.picker().addClass('datepicker-dropdown');
21048 this.startViewMode = this.viewMode;
21050 if(this.singleMode){
21051 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21052 v.setVisibilityMode(Roo.Element.DISPLAY);
21056 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21057 v.setStyle('width', '189px');
21061 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21062 if(!this.calendarWeeks){
21067 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21068 v.attr('colspan', function(i, val){
21069 return parseInt(val) + 1;
21074 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21076 this.setStartDate(this.startDate);
21077 this.setEndDate(this.endDate);
21079 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21086 if(this.isInline) {
21091 picker : function()
21093 return this.pickerEl;
21094 // return this.el.select('.datepicker', true).first();
21097 fillDow: function()
21099 var dowCnt = this.weekStart;
21108 if(this.calendarWeeks){
21116 while (dowCnt < this.weekStart + 7) {
21120 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21124 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21127 fillMonths: function()
21130 var months = this.picker().select('>.datepicker-months td', true).first();
21132 months.dom.innerHTML = '';
21138 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21141 months.createChild(month);
21148 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;
21150 if (this.date < this.startDate) {
21151 this.viewDate = new Date(this.startDate);
21152 } else if (this.date > this.endDate) {
21153 this.viewDate = new Date(this.endDate);
21155 this.viewDate = new Date(this.date);
21163 var d = new Date(this.viewDate),
21164 year = d.getUTCFullYear(),
21165 month = d.getUTCMonth(),
21166 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21167 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21168 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21169 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21170 currentDate = this.date && this.date.valueOf(),
21171 today = this.UTCToday();
21173 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21175 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21177 // this.picker.select('>tfoot th.today').
21178 // .text(dates[this.language].today)
21179 // .toggle(this.todayBtn !== false);
21181 this.updateNavArrows();
21184 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21186 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21188 prevMonth.setUTCDate(day);
21190 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21192 var nextMonth = new Date(prevMonth);
21194 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21196 nextMonth = nextMonth.valueOf();
21198 var fillMonths = false;
21200 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21202 while(prevMonth.valueOf() <= nextMonth) {
21205 if (prevMonth.getUTCDay() === this.weekStart) {
21207 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21215 if(this.calendarWeeks){
21216 // ISO 8601: First week contains first thursday.
21217 // ISO also states week starts on Monday, but we can be more abstract here.
21219 // Start of current week: based on weekstart/current date
21220 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21221 // Thursday of this week
21222 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21223 // First Thursday of year, year from thursday
21224 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21225 // Calendar week: ms between thursdays, div ms per day, div 7 days
21226 calWeek = (th - yth) / 864e5 / 7 + 1;
21228 fillMonths.cn.push({
21236 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21238 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21241 if (this.todayHighlight &&
21242 prevMonth.getUTCFullYear() == today.getFullYear() &&
21243 prevMonth.getUTCMonth() == today.getMonth() &&
21244 prevMonth.getUTCDate() == today.getDate()) {
21245 clsName += ' today';
21248 if (currentDate && prevMonth.valueOf() === currentDate) {
21249 clsName += ' active';
21252 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21253 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21254 clsName += ' disabled';
21257 fillMonths.cn.push({
21259 cls: 'day ' + clsName,
21260 html: prevMonth.getDate()
21263 prevMonth.setDate(prevMonth.getDate()+1);
21266 var currentYear = this.date && this.date.getUTCFullYear();
21267 var currentMonth = this.date && this.date.getUTCMonth();
21269 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21271 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21272 v.removeClass('active');
21274 if(currentYear === year && k === currentMonth){
21275 v.addClass('active');
21278 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21279 v.addClass('disabled');
21285 year = parseInt(year/10, 10) * 10;
21287 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21289 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21292 for (var i = -1; i < 11; i++) {
21293 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21295 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21303 showMode: function(dir)
21306 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21309 Roo.each(this.picker().select('>div',true).elements, function(v){
21310 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21313 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21318 if(this.isInline) {
21322 this.picker().removeClass(['bottom', 'top']);
21324 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21326 * place to the top of element!
21330 this.picker().addClass('top');
21331 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21336 this.picker().addClass('bottom');
21338 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21341 parseDate : function(value)
21343 if(!value || value instanceof Date){
21346 var v = Date.parseDate(value, this.format);
21347 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21348 v = Date.parseDate(value, 'Y-m-d');
21350 if(!v && this.altFormats){
21351 if(!this.altFormatsArray){
21352 this.altFormatsArray = this.altFormats.split("|");
21354 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21355 v = Date.parseDate(value, this.altFormatsArray[i]);
21361 formatDate : function(date, fmt)
21363 return (!date || !(date instanceof Date)) ?
21364 date : date.dateFormat(fmt || this.format);
21367 onFocus : function()
21369 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21373 onBlur : function()
21375 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21377 var d = this.inputEl().getValue();
21384 showPopup : function()
21386 this.picker().show();
21390 this.fireEvent('showpopup', this, this.date);
21393 hidePopup : function()
21395 if(this.isInline) {
21398 this.picker().hide();
21399 this.viewMode = this.startViewMode;
21402 this.fireEvent('hidepopup', this, this.date);
21406 onMousedown: function(e)
21408 e.stopPropagation();
21409 e.preventDefault();
21414 Roo.bootstrap.DateField.superclass.keyup.call(this);
21418 setValue: function(v)
21420 if(this.fireEvent('beforeselect', this, v) !== false){
21421 var d = new Date(this.parseDate(v) ).clearTime();
21423 if(isNaN(d.getTime())){
21424 this.date = this.viewDate = '';
21425 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21429 v = this.formatDate(d);
21431 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21433 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21437 this.fireEvent('select', this, this.date);
21441 getValue: function()
21443 return this.formatDate(this.date);
21446 fireKey: function(e)
21448 if (!this.picker().isVisible()){
21449 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21455 var dateChanged = false,
21457 newDate, newViewDate;
21462 e.preventDefault();
21466 if (!this.keyboardNavigation) {
21469 dir = e.keyCode == 37 ? -1 : 1;
21472 newDate = this.moveYear(this.date, dir);
21473 newViewDate = this.moveYear(this.viewDate, dir);
21474 } else if (e.shiftKey){
21475 newDate = this.moveMonth(this.date, dir);
21476 newViewDate = this.moveMonth(this.viewDate, dir);
21478 newDate = new Date(this.date);
21479 newDate.setUTCDate(this.date.getUTCDate() + dir);
21480 newViewDate = new Date(this.viewDate);
21481 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21483 if (this.dateWithinRange(newDate)){
21484 this.date = newDate;
21485 this.viewDate = newViewDate;
21486 this.setValue(this.formatDate(this.date));
21488 e.preventDefault();
21489 dateChanged = true;
21494 if (!this.keyboardNavigation) {
21497 dir = e.keyCode == 38 ? -1 : 1;
21499 newDate = this.moveYear(this.date, dir);
21500 newViewDate = this.moveYear(this.viewDate, dir);
21501 } else if (e.shiftKey){
21502 newDate = this.moveMonth(this.date, dir);
21503 newViewDate = this.moveMonth(this.viewDate, dir);
21505 newDate = new Date(this.date);
21506 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21507 newViewDate = new Date(this.viewDate);
21508 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21510 if (this.dateWithinRange(newDate)){
21511 this.date = newDate;
21512 this.viewDate = newViewDate;
21513 this.setValue(this.formatDate(this.date));
21515 e.preventDefault();
21516 dateChanged = true;
21520 this.setValue(this.formatDate(this.date));
21522 e.preventDefault();
21525 this.setValue(this.formatDate(this.date));
21539 onClick: function(e)
21541 e.stopPropagation();
21542 e.preventDefault();
21544 var target = e.getTarget();
21546 if(target.nodeName.toLowerCase() === 'i'){
21547 target = Roo.get(target).dom.parentNode;
21550 var nodeName = target.nodeName;
21551 var className = target.className;
21552 var html = target.innerHTML;
21553 //Roo.log(nodeName);
21555 switch(nodeName.toLowerCase()) {
21557 switch(className) {
21563 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21564 switch(this.viewMode){
21566 this.viewDate = this.moveMonth(this.viewDate, dir);
21570 this.viewDate = this.moveYear(this.viewDate, dir);
21576 var date = new Date();
21577 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21579 this.setValue(this.formatDate(this.date));
21586 if (className.indexOf('disabled') < 0) {
21587 this.viewDate.setUTCDate(1);
21588 if (className.indexOf('month') > -1) {
21589 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21591 var year = parseInt(html, 10) || 0;
21592 this.viewDate.setUTCFullYear(year);
21596 if(this.singleMode){
21597 this.setValue(this.formatDate(this.viewDate));
21608 //Roo.log(className);
21609 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21610 var day = parseInt(html, 10) || 1;
21611 var year = (this.viewDate || new Date()).getUTCFullYear(),
21612 month = (this.viewDate || new Date()).getUTCMonth();
21614 if (className.indexOf('old') > -1) {
21621 } else if (className.indexOf('new') > -1) {
21629 //Roo.log([year,month,day]);
21630 this.date = this.UTCDate(year, month, day,0,0,0,0);
21631 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21633 //Roo.log(this.formatDate(this.date));
21634 this.setValue(this.formatDate(this.date));
21641 setStartDate: function(startDate)
21643 this.startDate = startDate || -Infinity;
21644 if (this.startDate !== -Infinity) {
21645 this.startDate = this.parseDate(this.startDate);
21648 this.updateNavArrows();
21651 setEndDate: function(endDate)
21653 this.endDate = endDate || Infinity;
21654 if (this.endDate !== Infinity) {
21655 this.endDate = this.parseDate(this.endDate);
21658 this.updateNavArrows();
21661 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21663 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21664 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21665 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21667 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21668 return parseInt(d, 10);
21671 this.updateNavArrows();
21674 updateNavArrows: function()
21676 if(this.singleMode){
21680 var d = new Date(this.viewDate),
21681 year = d.getUTCFullYear(),
21682 month = d.getUTCMonth();
21684 Roo.each(this.picker().select('.prev', true).elements, function(v){
21686 switch (this.viewMode) {
21689 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21695 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21702 Roo.each(this.picker().select('.next', true).elements, function(v){
21704 switch (this.viewMode) {
21707 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21713 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21721 moveMonth: function(date, dir)
21726 var new_date = new Date(date.valueOf()),
21727 day = new_date.getUTCDate(),
21728 month = new_date.getUTCMonth(),
21729 mag = Math.abs(dir),
21731 dir = dir > 0 ? 1 : -1;
21734 // If going back one month, make sure month is not current month
21735 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21737 return new_date.getUTCMonth() == month;
21739 // If going forward one month, make sure month is as expected
21740 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21742 return new_date.getUTCMonth() != new_month;
21744 new_month = month + dir;
21745 new_date.setUTCMonth(new_month);
21746 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21747 if (new_month < 0 || new_month > 11) {
21748 new_month = (new_month + 12) % 12;
21751 // For magnitudes >1, move one month at a time...
21752 for (var i=0; i<mag; i++) {
21753 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21754 new_date = this.moveMonth(new_date, dir);
21756 // ...then reset the day, keeping it in the new month
21757 new_month = new_date.getUTCMonth();
21758 new_date.setUTCDate(day);
21760 return new_month != new_date.getUTCMonth();
21763 // Common date-resetting loop -- if date is beyond end of month, make it
21766 new_date.setUTCDate(--day);
21767 new_date.setUTCMonth(new_month);
21772 moveYear: function(date, dir)
21774 return this.moveMonth(date, dir*12);
21777 dateWithinRange: function(date)
21779 return date >= this.startDate && date <= this.endDate;
21785 this.picker().remove();
21788 validateValue : function(value)
21790 if(this.getVisibilityEl().hasClass('hidden')){
21794 if(value.length < 1) {
21795 if(this.allowBlank){
21801 if(value.length < this.minLength){
21804 if(value.length > this.maxLength){
21808 var vt = Roo.form.VTypes;
21809 if(!vt[this.vtype](value, this)){
21813 if(typeof this.validator == "function"){
21814 var msg = this.validator(value);
21820 if(this.regex && !this.regex.test(value)){
21824 if(typeof(this.parseDate(value)) == 'undefined'){
21828 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21832 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21842 this.date = this.viewDate = '';
21844 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21849 Roo.apply(Roo.bootstrap.DateField, {
21860 html: '<i class="fa fa-arrow-left"/>'
21870 html: '<i class="fa fa-arrow-right"/>'
21912 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21913 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21914 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21915 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21916 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21929 navFnc: 'FullYear',
21934 navFnc: 'FullYear',
21939 Roo.apply(Roo.bootstrap.DateField, {
21943 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21947 cls: 'datepicker-days',
21951 cls: 'table-condensed',
21953 Roo.bootstrap.DateField.head,
21957 Roo.bootstrap.DateField.footer
21964 cls: 'datepicker-months',
21968 cls: 'table-condensed',
21970 Roo.bootstrap.DateField.head,
21971 Roo.bootstrap.DateField.content,
21972 Roo.bootstrap.DateField.footer
21979 cls: 'datepicker-years',
21983 cls: 'table-condensed',
21985 Roo.bootstrap.DateField.head,
21986 Roo.bootstrap.DateField.content,
21987 Roo.bootstrap.DateField.footer
22006 * @class Roo.bootstrap.TimeField
22007 * @extends Roo.bootstrap.Input
22008 * Bootstrap DateField class
22012 * Create a new TimeField
22013 * @param {Object} config The config object
22016 Roo.bootstrap.TimeField = function(config){
22017 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22021 * Fires when this field show.
22022 * @param {Roo.bootstrap.DateField} thisthis
22023 * @param {Mixed} date The date value
22028 * Fires when this field hide.
22029 * @param {Roo.bootstrap.DateField} this
22030 * @param {Mixed} date The date value
22035 * Fires when select a date.
22036 * @param {Roo.bootstrap.DateField} this
22037 * @param {Mixed} date The date value
22043 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22046 * @cfg {String} format
22047 * The default time format string which can be overriden for localization support. The format must be
22048 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22052 getAutoCreate : function()
22054 this.after = '<i class="fa far fa-clock"></i>';
22055 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22059 onRender: function(ct, position)
22062 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22064 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22066 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22068 this.pop = this.picker().select('>.datepicker-time',true).first();
22069 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22071 this.picker().on('mousedown', this.onMousedown, this);
22072 this.picker().on('click', this.onClick, this);
22074 this.picker().addClass('datepicker-dropdown');
22079 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22080 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22081 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22082 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22083 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22084 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22088 fireKey: function(e){
22089 if (!this.picker().isVisible()){
22090 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22096 e.preventDefault();
22104 this.onTogglePeriod();
22107 this.onIncrementMinutes();
22110 this.onDecrementMinutes();
22119 onClick: function(e) {
22120 e.stopPropagation();
22121 e.preventDefault();
22124 picker : function()
22126 return this.pickerEl;
22129 fillTime: function()
22131 var time = this.pop.select('tbody', true).first();
22133 time.dom.innerHTML = '';
22148 cls: 'hours-up fa fas fa-chevron-up'
22168 cls: 'minutes-up fa fas fa-chevron-up'
22189 cls: 'timepicker-hour',
22204 cls: 'timepicker-minute',
22219 cls: 'btn btn-primary period',
22241 cls: 'hours-down fa fas fa-chevron-down'
22261 cls: 'minutes-down fa fas fa-chevron-down'
22279 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22286 var hours = this.time.getHours();
22287 var minutes = this.time.getMinutes();
22300 hours = hours - 12;
22304 hours = '0' + hours;
22308 minutes = '0' + minutes;
22311 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22312 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22313 this.pop.select('button', true).first().dom.innerHTML = period;
22319 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22321 var cls = ['bottom'];
22323 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22330 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22334 //this.picker().setXY(20000,20000);
22335 this.picker().addClass(cls.join('-'));
22339 Roo.each(cls, function(c){
22344 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22345 //_this.picker().setTop(_this.inputEl().getHeight());
22349 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22351 //_this.picker().setTop(0 - _this.picker().getHeight());
22356 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22360 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22368 onFocus : function()
22370 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22374 onBlur : function()
22376 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22382 this.picker().show();
22387 this.fireEvent('show', this, this.date);
22392 this.picker().hide();
22395 this.fireEvent('hide', this, this.date);
22398 setTime : function()
22401 this.setValue(this.time.format(this.format));
22403 this.fireEvent('select', this, this.date);
22408 onMousedown: function(e){
22409 e.stopPropagation();
22410 e.preventDefault();
22413 onIncrementHours: function()
22415 Roo.log('onIncrementHours');
22416 this.time = this.time.add(Date.HOUR, 1);
22421 onDecrementHours: function()
22423 Roo.log('onDecrementHours');
22424 this.time = this.time.add(Date.HOUR, -1);
22428 onIncrementMinutes: function()
22430 Roo.log('onIncrementMinutes');
22431 this.time = this.time.add(Date.MINUTE, 1);
22435 onDecrementMinutes: function()
22437 Roo.log('onDecrementMinutes');
22438 this.time = this.time.add(Date.MINUTE, -1);
22442 onTogglePeriod: function()
22444 Roo.log('onTogglePeriod');
22445 this.time = this.time.add(Date.HOUR, 12);
22453 Roo.apply(Roo.bootstrap.TimeField, {
22457 cls: 'datepicker dropdown-menu',
22461 cls: 'datepicker-time',
22465 cls: 'table-condensed',
22494 cls: 'btn btn-info ok',
22522 * @class Roo.bootstrap.MonthField
22523 * @extends Roo.bootstrap.Input
22524 * Bootstrap MonthField class
22526 * @cfg {String} language default en
22529 * Create a new MonthField
22530 * @param {Object} config The config object
22533 Roo.bootstrap.MonthField = function(config){
22534 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22539 * Fires when this field show.
22540 * @param {Roo.bootstrap.MonthField} this
22541 * @param {Mixed} date The date value
22546 * Fires when this field hide.
22547 * @param {Roo.bootstrap.MonthField} this
22548 * @param {Mixed} date The date value
22553 * Fires when select a date.
22554 * @param {Roo.bootstrap.MonthField} this
22555 * @param {String} oldvalue The old value
22556 * @param {String} newvalue The new value
22562 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22564 onRender: function(ct, position)
22567 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22569 this.language = this.language || 'en';
22570 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22571 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22573 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22574 this.isInline = false;
22575 this.isInput = true;
22576 this.component = this.el.select('.add-on', true).first() || false;
22577 this.component = (this.component && this.component.length === 0) ? false : this.component;
22578 this.hasInput = this.component && this.inputEL().length;
22580 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22582 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22584 this.picker().on('mousedown', this.onMousedown, this);
22585 this.picker().on('click', this.onClick, this);
22587 this.picker().addClass('datepicker-dropdown');
22589 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22590 v.setStyle('width', '189px');
22597 if(this.isInline) {
22603 setValue: function(v, suppressEvent)
22605 var o = this.getValue();
22607 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22611 if(suppressEvent !== true){
22612 this.fireEvent('select', this, o, v);
22617 getValue: function()
22622 onClick: function(e)
22624 e.stopPropagation();
22625 e.preventDefault();
22627 var target = e.getTarget();
22629 if(target.nodeName.toLowerCase() === 'i'){
22630 target = Roo.get(target).dom.parentNode;
22633 var nodeName = target.nodeName;
22634 var className = target.className;
22635 var html = target.innerHTML;
22637 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22641 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22643 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22649 picker : function()
22651 return this.pickerEl;
22654 fillMonths: function()
22657 var months = this.picker().select('>.datepicker-months td', true).first();
22659 months.dom.innerHTML = '';
22665 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22668 months.createChild(month);
22677 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22678 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22681 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22682 e.removeClass('active');
22684 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22685 e.addClass('active');
22692 if(this.isInline) {
22696 this.picker().removeClass(['bottom', 'top']);
22698 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22700 * place to the top of element!
22704 this.picker().addClass('top');
22705 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22710 this.picker().addClass('bottom');
22712 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22715 onFocus : function()
22717 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22721 onBlur : function()
22723 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22725 var d = this.inputEl().getValue();
22734 this.picker().show();
22735 this.picker().select('>.datepicker-months', true).first().show();
22739 this.fireEvent('show', this, this.date);
22744 if(this.isInline) {
22747 this.picker().hide();
22748 this.fireEvent('hide', this, this.date);
22752 onMousedown: function(e)
22754 e.stopPropagation();
22755 e.preventDefault();
22760 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22764 fireKey: function(e)
22766 if (!this.picker().isVisible()){
22767 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22778 e.preventDefault();
22782 dir = e.keyCode == 37 ? -1 : 1;
22784 this.vIndex = this.vIndex + dir;
22786 if(this.vIndex < 0){
22790 if(this.vIndex > 11){
22794 if(isNaN(this.vIndex)){
22798 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22804 dir = e.keyCode == 38 ? -1 : 1;
22806 this.vIndex = this.vIndex + dir * 4;
22808 if(this.vIndex < 0){
22812 if(this.vIndex > 11){
22816 if(isNaN(this.vIndex)){
22820 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22825 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22826 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22830 e.preventDefault();
22833 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22834 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22850 this.picker().remove();
22855 Roo.apply(Roo.bootstrap.MonthField, {
22874 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22875 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22880 Roo.apply(Roo.bootstrap.MonthField, {
22884 cls: 'datepicker dropdown-menu roo-dynamic',
22888 cls: 'datepicker-months',
22892 cls: 'table-condensed',
22894 Roo.bootstrap.DateField.content
22914 * @class Roo.bootstrap.CheckBox
22915 * @extends Roo.bootstrap.Input
22916 * Bootstrap CheckBox class
22918 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22919 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22920 * @cfg {String} boxLabel The text that appears beside the checkbox
22921 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22922 * @cfg {Boolean} checked initnal the element
22923 * @cfg {Boolean} inline inline the element (default false)
22924 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22925 * @cfg {String} tooltip label tooltip
22928 * Create a new CheckBox
22929 * @param {Object} config The config object
22932 Roo.bootstrap.CheckBox = function(config){
22933 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22938 * Fires when the element is checked or unchecked.
22939 * @param {Roo.bootstrap.CheckBox} this This input
22940 * @param {Boolean} checked The new checked value
22945 * Fires when the element is click.
22946 * @param {Roo.bootstrap.CheckBox} this This input
22953 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22955 inputType: 'checkbox',
22964 // checkbox success does not make any sense really..
22969 getAutoCreate : function()
22971 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22977 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22980 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22986 type : this.inputType,
22987 value : this.inputValue,
22988 cls : 'roo-' + this.inputType, //'form-box',
22989 placeholder : this.placeholder || ''
22993 if(this.inputType != 'radio'){
22997 cls : 'roo-hidden-value',
22998 value : this.checked ? this.inputValue : this.valueOff
23003 if (this.weight) { // Validity check?
23004 cfg.cls += " " + this.inputType + "-" + this.weight;
23007 if (this.disabled) {
23008 input.disabled=true;
23012 input.checked = this.checked;
23017 input.name = this.name;
23019 if(this.inputType != 'radio'){
23020 hidden.name = this.name;
23021 input.name = '_hidden_' + this.name;
23026 input.cls += ' input-' + this.size;
23031 ['xs','sm','md','lg'].map(function(size){
23032 if (settings[size]) {
23033 cfg.cls += ' col-' + size + '-' + settings[size];
23037 var inputblock = input;
23039 if (this.before || this.after) {
23042 cls : 'input-group',
23047 inputblock.cn.push({
23049 cls : 'input-group-addon',
23054 inputblock.cn.push(input);
23056 if(this.inputType != 'radio'){
23057 inputblock.cn.push(hidden);
23061 inputblock.cn.push({
23063 cls : 'input-group-addon',
23069 var boxLabelCfg = false;
23075 //'for': id, // box label is handled by onclick - so no for...
23077 html: this.boxLabel
23080 boxLabelCfg.tooltip = this.tooltip;
23086 if (align ==='left' && this.fieldLabel.length) {
23087 // Roo.log("left and has label");
23092 cls : 'control-label',
23093 html : this.fieldLabel
23104 cfg.cn[1].cn.push(boxLabelCfg);
23107 if(this.labelWidth > 12){
23108 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23111 if(this.labelWidth < 13 && this.labelmd == 0){
23112 this.labelmd = this.labelWidth;
23115 if(this.labellg > 0){
23116 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23117 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23120 if(this.labelmd > 0){
23121 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23122 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23125 if(this.labelsm > 0){
23126 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23127 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23130 if(this.labelxs > 0){
23131 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23132 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23135 } else if ( this.fieldLabel.length) {
23136 // Roo.log(" label");
23140 tag: this.boxLabel ? 'span' : 'label',
23142 cls: 'control-label box-input-label',
23143 //cls : 'input-group-addon',
23144 html : this.fieldLabel
23151 cfg.cn.push(boxLabelCfg);
23156 // Roo.log(" no label && no align");
23157 cfg.cn = [ inputblock ] ;
23159 cfg.cn.push(boxLabelCfg);
23167 if(this.inputType != 'radio'){
23168 cfg.cn.push(hidden);
23176 * return the real input element.
23178 inputEl: function ()
23180 return this.el.select('input.roo-' + this.inputType,true).first();
23182 hiddenEl: function ()
23184 return this.el.select('input.roo-hidden-value',true).first();
23187 labelEl: function()
23189 return this.el.select('label.control-label',true).first();
23191 /* depricated... */
23195 return this.labelEl();
23198 boxLabelEl: function()
23200 return this.el.select('label.box-label',true).first();
23203 initEvents : function()
23205 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23207 this.inputEl().on('click', this.onClick, this);
23209 if (this.boxLabel) {
23210 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23213 this.startValue = this.getValue();
23216 Roo.bootstrap.CheckBox.register(this);
23220 onClick : function(e)
23222 if(this.fireEvent('click', this, e) !== false){
23223 this.setChecked(!this.checked);
23228 setChecked : function(state,suppressEvent)
23230 this.startValue = this.getValue();
23232 if(this.inputType == 'radio'){
23234 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23235 e.dom.checked = false;
23238 this.inputEl().dom.checked = true;
23240 this.inputEl().dom.value = this.inputValue;
23242 if(suppressEvent !== true){
23243 this.fireEvent('check', this, true);
23251 this.checked = state;
23253 this.inputEl().dom.checked = state;
23256 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23258 if(suppressEvent !== true){
23259 this.fireEvent('check', this, state);
23265 getValue : function()
23267 if(this.inputType == 'radio'){
23268 return this.getGroupValue();
23271 return this.hiddenEl().dom.value;
23275 getGroupValue : function()
23277 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23281 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23284 setValue : function(v,suppressEvent)
23286 if(this.inputType == 'radio'){
23287 this.setGroupValue(v, suppressEvent);
23291 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23296 setGroupValue : function(v, suppressEvent)
23298 this.startValue = this.getValue();
23300 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23301 e.dom.checked = false;
23303 if(e.dom.value == v){
23304 e.dom.checked = true;
23308 if(suppressEvent !== true){
23309 this.fireEvent('check', this, true);
23317 validate : function()
23319 if(this.getVisibilityEl().hasClass('hidden')){
23325 (this.inputType == 'radio' && this.validateRadio()) ||
23326 (this.inputType == 'checkbox' && this.validateCheckbox())
23332 this.markInvalid();
23336 validateRadio : function()
23338 if(this.getVisibilityEl().hasClass('hidden')){
23342 if(this.allowBlank){
23348 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23349 if(!e.dom.checked){
23361 validateCheckbox : function()
23364 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23365 //return (this.getValue() == this.inputValue) ? true : false;
23368 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23376 for(var i in group){
23377 if(group[i].el.isVisible(true)){
23385 for(var i in group){
23390 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23397 * Mark this field as valid
23399 markValid : function()
23403 this.fireEvent('valid', this);
23405 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23408 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23415 if(this.inputType == 'radio'){
23416 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23417 var fg = e.findParent('.form-group', false, true);
23418 if (Roo.bootstrap.version == 3) {
23419 fg.removeClass([_this.invalidClass, _this.validClass]);
23420 fg.addClass(_this.validClass);
23422 fg.removeClass(['is-valid', 'is-invalid']);
23423 fg.addClass('is-valid');
23431 var fg = this.el.findParent('.form-group', false, true);
23432 if (Roo.bootstrap.version == 3) {
23433 fg.removeClass([this.invalidClass, this.validClass]);
23434 fg.addClass(this.validClass);
23436 fg.removeClass(['is-valid', 'is-invalid']);
23437 fg.addClass('is-valid');
23442 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23448 for(var i in group){
23449 var fg = group[i].el.findParent('.form-group', false, true);
23450 if (Roo.bootstrap.version == 3) {
23451 fg.removeClass([this.invalidClass, this.validClass]);
23452 fg.addClass(this.validClass);
23454 fg.removeClass(['is-valid', 'is-invalid']);
23455 fg.addClass('is-valid');
23461 * Mark this field as invalid
23462 * @param {String} msg The validation message
23464 markInvalid : function(msg)
23466 if(this.allowBlank){
23472 this.fireEvent('invalid', this, msg);
23474 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23477 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23481 label.markInvalid();
23484 if(this.inputType == 'radio'){
23486 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23487 var fg = e.findParent('.form-group', false, true);
23488 if (Roo.bootstrap.version == 3) {
23489 fg.removeClass([_this.invalidClass, _this.validClass]);
23490 fg.addClass(_this.invalidClass);
23492 fg.removeClass(['is-invalid', 'is-valid']);
23493 fg.addClass('is-invalid');
23501 var fg = this.el.findParent('.form-group', false, true);
23502 if (Roo.bootstrap.version == 3) {
23503 fg.removeClass([_this.invalidClass, _this.validClass]);
23504 fg.addClass(_this.invalidClass);
23506 fg.removeClass(['is-invalid', 'is-valid']);
23507 fg.addClass('is-invalid');
23512 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23518 for(var i in group){
23519 var fg = group[i].el.findParent('.form-group', false, true);
23520 if (Roo.bootstrap.version == 3) {
23521 fg.removeClass([_this.invalidClass, _this.validClass]);
23522 fg.addClass(_this.invalidClass);
23524 fg.removeClass(['is-invalid', 'is-valid']);
23525 fg.addClass('is-invalid');
23531 clearInvalid : function()
23533 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23535 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23537 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23539 if (label && label.iconEl) {
23540 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23541 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23545 disable : function()
23547 if(this.inputType != 'radio'){
23548 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23555 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23556 _this.getActionEl().addClass(this.disabledClass);
23557 e.dom.disabled = true;
23561 this.disabled = true;
23562 this.fireEvent("disable", this);
23566 enable : function()
23568 if(this.inputType != 'radio'){
23569 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23576 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23577 _this.getActionEl().removeClass(this.disabledClass);
23578 e.dom.disabled = false;
23582 this.disabled = false;
23583 this.fireEvent("enable", this);
23587 setBoxLabel : function(v)
23592 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23598 Roo.apply(Roo.bootstrap.CheckBox, {
23603 * register a CheckBox Group
23604 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23606 register : function(checkbox)
23608 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23609 this.groups[checkbox.groupId] = {};
23612 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23616 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23620 * fetch a CheckBox Group based on the group ID
23621 * @param {string} the group ID
23622 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23624 get: function(groupId) {
23625 if (typeof(this.groups[groupId]) == 'undefined') {
23629 return this.groups[groupId] ;
23642 * @class Roo.bootstrap.Radio
23643 * @extends Roo.bootstrap.Component
23644 * Bootstrap Radio class
23645 * @cfg {String} boxLabel - the label associated
23646 * @cfg {String} value - the value of radio
23649 * Create a new Radio
23650 * @param {Object} config The config object
23652 Roo.bootstrap.Radio = function(config){
23653 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23657 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23663 getAutoCreate : function()
23667 cls : 'form-group radio',
23672 html : this.boxLabel
23680 initEvents : function()
23682 this.parent().register(this);
23684 this.el.on('click', this.onClick, this);
23688 onClick : function(e)
23690 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23691 this.setChecked(true);
23695 setChecked : function(state, suppressEvent)
23697 this.parent().setValue(this.value, suppressEvent);
23701 setBoxLabel : function(v)
23706 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23721 * @class Roo.bootstrap.SecurePass
23722 * @extends Roo.bootstrap.Input
23723 * Bootstrap SecurePass class
23727 * Create a new SecurePass
23728 * @param {Object} config The config object
23731 Roo.bootstrap.SecurePass = function (config) {
23732 // these go here, so the translation tool can replace them..
23734 PwdEmpty: "Please type a password, and then retype it to confirm.",
23735 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23736 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23737 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23738 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23739 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23740 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23741 TooWeak: "Your password is Too Weak."
23743 this.meterLabel = "Password strength:";
23744 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23745 this.meterClass = [
23746 "roo-password-meter-tooweak",
23747 "roo-password-meter-weak",
23748 "roo-password-meter-medium",
23749 "roo-password-meter-strong",
23750 "roo-password-meter-grey"
23755 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23758 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23760 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23762 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23763 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23764 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23765 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23766 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23767 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23768 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23778 * @cfg {String/Object} Label for the strength meter (defaults to
23779 * 'Password strength:')
23784 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23785 * ['Weak', 'Medium', 'Strong'])
23788 pwdStrengths: false,
23801 initEvents: function ()
23803 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23805 if (this.el.is('input[type=password]') && Roo.isSafari) {
23806 this.el.on('keydown', this.SafariOnKeyDown, this);
23809 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23812 onRender: function (ct, position)
23814 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23815 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23816 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23818 this.trigger.createChild({
23823 cls: 'roo-password-meter-grey col-xs-12',
23826 //width: this.meterWidth + 'px'
23830 cls: 'roo-password-meter-text'
23836 if (this.hideTrigger) {
23837 this.trigger.setDisplayed(false);
23839 this.setSize(this.width || '', this.height || '');
23842 onDestroy: function ()
23844 if (this.trigger) {
23845 this.trigger.removeAllListeners();
23846 this.trigger.remove();
23849 this.wrap.remove();
23851 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23854 checkStrength: function ()
23856 var pwd = this.inputEl().getValue();
23857 if (pwd == this._lastPwd) {
23862 if (this.ClientSideStrongPassword(pwd)) {
23864 } else if (this.ClientSideMediumPassword(pwd)) {
23866 } else if (this.ClientSideWeakPassword(pwd)) {
23872 Roo.log('strength1: ' + strength);
23874 //var pm = this.trigger.child('div/div/div').dom;
23875 var pm = this.trigger.child('div/div');
23876 pm.removeClass(this.meterClass);
23877 pm.addClass(this.meterClass[strength]);
23880 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23882 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23884 this._lastPwd = pwd;
23888 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23890 this._lastPwd = '';
23892 var pm = this.trigger.child('div/div');
23893 pm.removeClass(this.meterClass);
23894 pm.addClass('roo-password-meter-grey');
23897 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23900 this.inputEl().dom.type='password';
23903 validateValue: function (value)
23905 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23908 if (value.length == 0) {
23909 if (this.allowBlank) {
23910 this.clearInvalid();
23914 this.markInvalid(this.errors.PwdEmpty);
23915 this.errorMsg = this.errors.PwdEmpty;
23923 if (!value.match(/[\x21-\x7e]+/)) {
23924 this.markInvalid(this.errors.PwdBadChar);
23925 this.errorMsg = this.errors.PwdBadChar;
23928 if (value.length < 6) {
23929 this.markInvalid(this.errors.PwdShort);
23930 this.errorMsg = this.errors.PwdShort;
23933 if (value.length > 16) {
23934 this.markInvalid(this.errors.PwdLong);
23935 this.errorMsg = this.errors.PwdLong;
23939 if (this.ClientSideStrongPassword(value)) {
23941 } else if (this.ClientSideMediumPassword(value)) {
23943 } else if (this.ClientSideWeakPassword(value)) {
23950 if (strength < 2) {
23951 //this.markInvalid(this.errors.TooWeak);
23952 this.errorMsg = this.errors.TooWeak;
23957 console.log('strength2: ' + strength);
23959 //var pm = this.trigger.child('div/div/div').dom;
23961 var pm = this.trigger.child('div/div');
23962 pm.removeClass(this.meterClass);
23963 pm.addClass(this.meterClass[strength]);
23965 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23967 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23969 this.errorMsg = '';
23973 CharacterSetChecks: function (type)
23976 this.fResult = false;
23979 isctype: function (character, type)
23982 case this.kCapitalLetter:
23983 if (character >= 'A' && character <= 'Z') {
23988 case this.kSmallLetter:
23989 if (character >= 'a' && character <= 'z') {
23995 if (character >= '0' && character <= '9') {
24000 case this.kPunctuation:
24001 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24012 IsLongEnough: function (pwd, size)
24014 return !(pwd == null || isNaN(size) || pwd.length < size);
24017 SpansEnoughCharacterSets: function (word, nb)
24019 if (!this.IsLongEnough(word, nb))
24024 var characterSetChecks = new Array(
24025 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24026 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24029 for (var index = 0; index < word.length; ++index) {
24030 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24031 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24032 characterSetChecks[nCharSet].fResult = true;
24039 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24040 if (characterSetChecks[nCharSet].fResult) {
24045 if (nCharSets < nb) {
24051 ClientSideStrongPassword: function (pwd)
24053 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24056 ClientSideMediumPassword: function (pwd)
24058 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24061 ClientSideWeakPassword: function (pwd)
24063 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24066 })//<script type="text/javascript">
24069 * Based Ext JS Library 1.1.1
24070 * Copyright(c) 2006-2007, Ext JS, LLC.
24076 * @class Roo.HtmlEditorCore
24077 * @extends Roo.Component
24078 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24080 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24083 Roo.HtmlEditorCore = function(config){
24086 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24091 * @event initialize
24092 * Fires when the editor is fully initialized (including the iframe)
24093 * @param {Roo.HtmlEditorCore} this
24098 * Fires when the editor is first receives the focus. Any insertion must wait
24099 * until after this event.
24100 * @param {Roo.HtmlEditorCore} this
24104 * @event beforesync
24105 * Fires before the textarea is updated with content from the editor iframe. Return false
24106 * to cancel the sync.
24107 * @param {Roo.HtmlEditorCore} this
24108 * @param {String} html
24112 * @event beforepush
24113 * Fires before the iframe editor is updated with content from the textarea. Return false
24114 * to cancel the push.
24115 * @param {Roo.HtmlEditorCore} this
24116 * @param {String} html
24121 * Fires when the textarea is updated with content from the editor iframe.
24122 * @param {Roo.HtmlEditorCore} this
24123 * @param {String} html
24128 * Fires when the iframe editor is updated with content from the textarea.
24129 * @param {Roo.HtmlEditorCore} this
24130 * @param {String} html
24135 * @event editorevent
24136 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24137 * @param {Roo.HtmlEditorCore} this
24143 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24145 // defaults : white / black...
24146 this.applyBlacklists();
24153 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24157 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24163 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24168 * @cfg {Number} height (in pixels)
24172 * @cfg {Number} width (in pixels)
24177 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24180 stylesheets: false,
24185 // private properties
24186 validationEvent : false,
24188 initialized : false,
24190 sourceEditMode : false,
24191 onFocus : Roo.emptyFn,
24193 hideMode:'offsets',
24197 // blacklist + whitelisted elements..
24204 * Protected method that will not generally be called directly. It
24205 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24206 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24208 getDocMarkup : function(){
24212 // inherit styels from page...??
24213 if (this.stylesheets === false) {
24215 Roo.get(document.head).select('style').each(function(node) {
24216 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24219 Roo.get(document.head).select('link').each(function(node) {
24220 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24223 } else if (!this.stylesheets.length) {
24225 st = '<style type="text/css">' +
24226 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24229 for (var i in this.stylesheets) {
24230 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24235 st += '<style type="text/css">' +
24236 'IMG { cursor: pointer } ' +
24239 var cls = 'roo-htmleditor-body';
24241 if(this.bodyCls.length){
24242 cls += ' ' + this.bodyCls;
24245 return '<html><head>' + st +
24246 //<style type="text/css">' +
24247 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24249 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24253 onRender : function(ct, position)
24256 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24257 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24260 this.el.dom.style.border = '0 none';
24261 this.el.dom.setAttribute('tabIndex', -1);
24262 this.el.addClass('x-hidden hide');
24266 if(Roo.isIE){ // fix IE 1px bogus margin
24267 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24271 this.frameId = Roo.id();
24275 var iframe = this.owner.wrap.createChild({
24277 cls: 'form-control', // bootstrap..
24279 name: this.frameId,
24280 frameBorder : 'no',
24281 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24286 this.iframe = iframe.dom;
24288 this.assignDocWin();
24290 this.doc.designMode = 'on';
24293 this.doc.write(this.getDocMarkup());
24297 var task = { // must defer to wait for browser to be ready
24299 //console.log("run task?" + this.doc.readyState);
24300 this.assignDocWin();
24301 if(this.doc.body || this.doc.readyState == 'complete'){
24303 this.doc.designMode="on";
24307 Roo.TaskMgr.stop(task);
24308 this.initEditor.defer(10, this);
24315 Roo.TaskMgr.start(task);
24320 onResize : function(w, h)
24322 Roo.log('resize: ' +w + ',' + h );
24323 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24327 if(typeof w == 'number'){
24329 this.iframe.style.width = w + 'px';
24331 if(typeof h == 'number'){
24333 this.iframe.style.height = h + 'px';
24335 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24342 * Toggles the editor between standard and source edit mode.
24343 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24345 toggleSourceEdit : function(sourceEditMode){
24347 this.sourceEditMode = sourceEditMode === true;
24349 if(this.sourceEditMode){
24351 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24354 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24355 //this.iframe.className = '';
24358 //this.setSize(this.owner.wrap.getSize());
24359 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24366 * Protected method that will not generally be called directly. If you need/want
24367 * custom HTML cleanup, this is the method you should override.
24368 * @param {String} html The HTML to be cleaned
24369 * return {String} The cleaned HTML
24371 cleanHtml : function(html){
24372 html = String(html);
24373 if(html.length > 5){
24374 if(Roo.isSafari){ // strip safari nonsense
24375 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24378 if(html == ' '){
24385 * HTML Editor -> Textarea
24386 * Protected method that will not generally be called directly. Syncs the contents
24387 * of the editor iframe with the textarea.
24389 syncValue : function(){
24390 if(this.initialized){
24391 var bd = (this.doc.body || this.doc.documentElement);
24392 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24393 var html = bd.innerHTML;
24395 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24396 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24398 html = '<div style="'+m[0]+'">' + html + '</div>';
24401 html = this.cleanHtml(html);
24402 // fix up the special chars.. normaly like back quotes in word...
24403 // however we do not want to do this with chinese..
24404 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24406 var cc = match.charCodeAt();
24408 // Get the character value, handling surrogate pairs
24409 if (match.length == 2) {
24410 // It's a surrogate pair, calculate the Unicode code point
24411 var high = match.charCodeAt(0) - 0xD800;
24412 var low = match.charCodeAt(1) - 0xDC00;
24413 cc = (high * 0x400) + low + 0x10000;
24415 (cc >= 0x4E00 && cc < 0xA000 ) ||
24416 (cc >= 0x3400 && cc < 0x4E00 ) ||
24417 (cc >= 0xf900 && cc < 0xfb00 )
24422 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24423 return "&#" + cc + ";";
24430 if(this.owner.fireEvent('beforesync', this, html) !== false){
24431 this.el.dom.value = html;
24432 this.owner.fireEvent('sync', this, html);
24438 * Protected method that will not generally be called directly. Pushes the value of the textarea
24439 * into the iframe editor.
24441 pushValue : function(){
24442 if(this.initialized){
24443 var v = this.el.dom.value.trim();
24445 // if(v.length < 1){
24449 if(this.owner.fireEvent('beforepush', this, v) !== false){
24450 var d = (this.doc.body || this.doc.documentElement);
24452 this.cleanUpPaste();
24453 this.el.dom.value = d.innerHTML;
24454 this.owner.fireEvent('push', this, v);
24460 deferFocus : function(){
24461 this.focus.defer(10, this);
24465 focus : function(){
24466 if(this.win && !this.sourceEditMode){
24473 assignDocWin: function()
24475 var iframe = this.iframe;
24478 this.doc = iframe.contentWindow.document;
24479 this.win = iframe.contentWindow;
24481 // if (!Roo.get(this.frameId)) {
24484 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24485 // this.win = Roo.get(this.frameId).dom.contentWindow;
24487 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24491 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24492 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24497 initEditor : function(){
24498 //console.log("INIT EDITOR");
24499 this.assignDocWin();
24503 this.doc.designMode="on";
24505 this.doc.write(this.getDocMarkup());
24508 var dbody = (this.doc.body || this.doc.documentElement);
24509 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24510 // this copies styles from the containing element into thsi one..
24511 // not sure why we need all of this..
24512 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24514 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24515 //ss['background-attachment'] = 'fixed'; // w3c
24516 dbody.bgProperties = 'fixed'; // ie
24517 //Roo.DomHelper.applyStyles(dbody, ss);
24518 Roo.EventManager.on(this.doc, {
24519 //'mousedown': this.onEditorEvent,
24520 'mouseup': this.onEditorEvent,
24521 'dblclick': this.onEditorEvent,
24522 'click': this.onEditorEvent,
24523 'keyup': this.onEditorEvent,
24528 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24530 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24531 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24533 this.initialized = true;
24535 this.owner.fireEvent('initialize', this);
24540 onDestroy : function(){
24546 //for (var i =0; i < this.toolbars.length;i++) {
24547 // // fixme - ask toolbars for heights?
24548 // this.toolbars[i].onDestroy();
24551 //this.wrap.dom.innerHTML = '';
24552 //this.wrap.remove();
24557 onFirstFocus : function(){
24559 this.assignDocWin();
24562 this.activated = true;
24565 if(Roo.isGecko){ // prevent silly gecko errors
24567 var s = this.win.getSelection();
24568 if(!s.focusNode || s.focusNode.nodeType != 3){
24569 var r = s.getRangeAt(0);
24570 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24575 this.execCmd('useCSS', true);
24576 this.execCmd('styleWithCSS', false);
24579 this.owner.fireEvent('activate', this);
24583 adjustFont: function(btn){
24584 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24585 //if(Roo.isSafari){ // safari
24588 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24589 if(Roo.isSafari){ // safari
24590 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24591 v = (v < 10) ? 10 : v;
24592 v = (v > 48) ? 48 : v;
24593 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24598 v = Math.max(1, v+adjust);
24600 this.execCmd('FontSize', v );
24603 onEditorEvent : function(e)
24605 this.owner.fireEvent('editorevent', this, e);
24606 // this.updateToolbar();
24607 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24610 insertTag : function(tg)
24612 // could be a bit smarter... -> wrap the current selected tRoo..
24613 if (tg.toLowerCase() == 'span' ||
24614 tg.toLowerCase() == 'code' ||
24615 tg.toLowerCase() == 'sup' ||
24616 tg.toLowerCase() == 'sub'
24619 range = this.createRange(this.getSelection());
24620 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24621 wrappingNode.appendChild(range.extractContents());
24622 range.insertNode(wrappingNode);
24629 this.execCmd("formatblock", tg);
24633 insertText : function(txt)
24637 var range = this.createRange();
24638 range.deleteContents();
24639 //alert(Sender.getAttribute('label'));
24641 range.insertNode(this.doc.createTextNode(txt));
24647 * Executes a Midas editor command on the editor document and performs necessary focus and
24648 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24649 * @param {String} cmd The Midas command
24650 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24652 relayCmd : function(cmd, value){
24654 this.execCmd(cmd, value);
24655 this.owner.fireEvent('editorevent', this);
24656 //this.updateToolbar();
24657 this.owner.deferFocus();
24661 * Executes a Midas editor command directly on the editor document.
24662 * For visual commands, you should use {@link #relayCmd} instead.
24663 * <b>This should only be called after the editor is initialized.</b>
24664 * @param {String} cmd The Midas command
24665 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24667 execCmd : function(cmd, value){
24668 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24675 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24677 * @param {String} text | dom node..
24679 insertAtCursor : function(text)
24682 if(!this.activated){
24688 var r = this.doc.selection.createRange();
24699 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24703 // from jquery ui (MIT licenced)
24705 var win = this.win;
24707 if (win.getSelection && win.getSelection().getRangeAt) {
24708 range = win.getSelection().getRangeAt(0);
24709 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24710 range.insertNode(node);
24711 } else if (win.document.selection && win.document.selection.createRange) {
24712 // no firefox support
24713 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24714 win.document.selection.createRange().pasteHTML(txt);
24716 // no firefox support
24717 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24718 this.execCmd('InsertHTML', txt);
24727 mozKeyPress : function(e){
24729 var c = e.getCharCode(), cmd;
24732 c = String.fromCharCode(c).toLowerCase();
24746 this.cleanUpPaste.defer(100, this);
24754 e.preventDefault();
24762 fixKeys : function(){ // load time branching for fastest keydown performance
24764 return function(e){
24765 var k = e.getKey(), r;
24768 r = this.doc.selection.createRange();
24771 r.pasteHTML('    ');
24778 r = this.doc.selection.createRange();
24780 var target = r.parentElement();
24781 if(!target || target.tagName.toLowerCase() != 'li'){
24783 r.pasteHTML('<br />');
24789 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24790 this.cleanUpPaste.defer(100, this);
24796 }else if(Roo.isOpera){
24797 return function(e){
24798 var k = e.getKey();
24802 this.execCmd('InsertHTML','    ');
24805 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24806 this.cleanUpPaste.defer(100, this);
24811 }else if(Roo.isSafari){
24812 return function(e){
24813 var k = e.getKey();
24817 this.execCmd('InsertText','\t');
24821 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24822 this.cleanUpPaste.defer(100, this);
24830 getAllAncestors: function()
24832 var p = this.getSelectedNode();
24835 a.push(p); // push blank onto stack..
24836 p = this.getParentElement();
24840 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24844 a.push(this.doc.body);
24848 lastSelNode : false,
24851 getSelection : function()
24853 this.assignDocWin();
24854 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24857 getSelectedNode: function()
24859 // this may only work on Gecko!!!
24861 // should we cache this!!!!
24866 var range = this.createRange(this.getSelection()).cloneRange();
24869 var parent = range.parentElement();
24871 var testRange = range.duplicate();
24872 testRange.moveToElementText(parent);
24873 if (testRange.inRange(range)) {
24876 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24879 parent = parent.parentElement;
24884 // is ancestor a text element.
24885 var ac = range.commonAncestorContainer;
24886 if (ac.nodeType == 3) {
24887 ac = ac.parentNode;
24890 var ar = ac.childNodes;
24893 var other_nodes = [];
24894 var has_other_nodes = false;
24895 for (var i=0;i<ar.length;i++) {
24896 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24899 // fullly contained node.
24901 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24906 // probably selected..
24907 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24908 other_nodes.push(ar[i]);
24912 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24917 has_other_nodes = true;
24919 if (!nodes.length && other_nodes.length) {
24920 nodes= other_nodes;
24922 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24928 createRange: function(sel)
24930 // this has strange effects when using with
24931 // top toolbar - not sure if it's a great idea.
24932 //this.editor.contentWindow.focus();
24933 if (typeof sel != "undefined") {
24935 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24937 return this.doc.createRange();
24940 return this.doc.createRange();
24943 getParentElement: function()
24946 this.assignDocWin();
24947 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24949 var range = this.createRange(sel);
24952 var p = range.commonAncestorContainer;
24953 while (p.nodeType == 3) { // text node
24964 * Range intersection.. the hard stuff...
24968 * [ -- selected range --- ]
24972 * if end is before start or hits it. fail.
24973 * if start is after end or hits it fail.
24975 * if either hits (but other is outside. - then it's not
24981 // @see http://www.thismuchiknow.co.uk/?p=64.
24982 rangeIntersectsNode : function(range, node)
24984 var nodeRange = node.ownerDocument.createRange();
24986 nodeRange.selectNode(node);
24988 nodeRange.selectNodeContents(node);
24991 var rangeStartRange = range.cloneRange();
24992 rangeStartRange.collapse(true);
24994 var rangeEndRange = range.cloneRange();
24995 rangeEndRange.collapse(false);
24997 var nodeStartRange = nodeRange.cloneRange();
24998 nodeStartRange.collapse(true);
25000 var nodeEndRange = nodeRange.cloneRange();
25001 nodeEndRange.collapse(false);
25003 return rangeStartRange.compareBoundaryPoints(
25004 Range.START_TO_START, nodeEndRange) == -1 &&
25005 rangeEndRange.compareBoundaryPoints(
25006 Range.START_TO_START, nodeStartRange) == 1;
25010 rangeCompareNode : function(range, node)
25012 var nodeRange = node.ownerDocument.createRange();
25014 nodeRange.selectNode(node);
25016 nodeRange.selectNodeContents(node);
25020 range.collapse(true);
25022 nodeRange.collapse(true);
25024 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25025 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25027 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25029 var nodeIsBefore = ss == 1;
25030 var nodeIsAfter = ee == -1;
25032 if (nodeIsBefore && nodeIsAfter) {
25035 if (!nodeIsBefore && nodeIsAfter) {
25036 return 1; //right trailed.
25039 if (nodeIsBefore && !nodeIsAfter) {
25040 return 2; // left trailed.
25046 // private? - in a new class?
25047 cleanUpPaste : function()
25049 // cleans up the whole document..
25050 Roo.log('cleanuppaste');
25052 this.cleanUpChildren(this.doc.body);
25053 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25054 if (clean != this.doc.body.innerHTML) {
25055 this.doc.body.innerHTML = clean;
25060 cleanWordChars : function(input) {// change the chars to hex code
25061 var he = Roo.HtmlEditorCore;
25063 var output = input;
25064 Roo.each(he.swapCodes, function(sw) {
25065 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25067 output = output.replace(swapper, sw[1]);
25074 cleanUpChildren : function (n)
25076 if (!n.childNodes.length) {
25079 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25080 this.cleanUpChild(n.childNodes[i]);
25087 cleanUpChild : function (node)
25090 //console.log(node);
25091 if (node.nodeName == "#text") {
25092 // clean up silly Windows -- stuff?
25095 if (node.nodeName == "#comment") {
25096 node.parentNode.removeChild(node);
25097 // clean up silly Windows -- stuff?
25100 var lcname = node.tagName.toLowerCase();
25101 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25102 // whitelist of tags..
25104 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25106 node.parentNode.removeChild(node);
25111 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25113 // spans with no attributes - just remove them..
25114 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25115 remove_keep_children = true;
25118 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25119 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25121 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25122 // remove_keep_children = true;
25125 if (remove_keep_children) {
25126 this.cleanUpChildren(node);
25127 // inserts everything just before this node...
25128 while (node.childNodes.length) {
25129 var cn = node.childNodes[0];
25130 node.removeChild(cn);
25131 node.parentNode.insertBefore(cn, node);
25133 node.parentNode.removeChild(node);
25137 if (!node.attributes || !node.attributes.length) {
25142 this.cleanUpChildren(node);
25146 function cleanAttr(n,v)
25149 if (v.match(/^\./) || v.match(/^\//)) {
25152 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25155 if (v.match(/^#/)) {
25158 if (v.match(/^\{/)) { // allow template editing.
25161 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25162 node.removeAttribute(n);
25166 var cwhite = this.cwhite;
25167 var cblack = this.cblack;
25169 function cleanStyle(n,v)
25171 if (v.match(/expression/)) { //XSS?? should we even bother..
25172 node.removeAttribute(n);
25176 var parts = v.split(/;/);
25179 Roo.each(parts, function(p) {
25180 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25184 var l = p.split(':').shift().replace(/\s+/g,'');
25185 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25187 if ( cwhite.length && cblack.indexOf(l) > -1) {
25188 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25189 //node.removeAttribute(n);
25193 // only allow 'c whitelisted system attributes'
25194 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25195 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25196 //node.removeAttribute(n);
25206 if (clean.length) {
25207 node.setAttribute(n, clean.join(';'));
25209 node.removeAttribute(n);
25215 for (var i = node.attributes.length-1; i > -1 ; i--) {
25216 var a = node.attributes[i];
25219 if (a.name.toLowerCase().substr(0,2)=='on') {
25220 node.removeAttribute(a.name);
25223 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25224 node.removeAttribute(a.name);
25227 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25228 cleanAttr(a.name,a.value); // fixme..
25231 if (a.name == 'style') {
25232 cleanStyle(a.name,a.value);
25235 /// clean up MS crap..
25236 // tecnically this should be a list of valid class'es..
25239 if (a.name == 'class') {
25240 if (a.value.match(/^Mso/)) {
25241 node.removeAttribute('class');
25244 if (a.value.match(/^body$/)) {
25245 node.removeAttribute('class');
25256 this.cleanUpChildren(node);
25262 * Clean up MS wordisms...
25264 cleanWord : function(node)
25267 this.cleanWord(this.doc.body);
25272 node.nodeName == 'SPAN' &&
25273 !node.hasAttributes() &&
25274 node.childNodes.length == 1 &&
25275 node.firstChild.nodeName == "#text"
25277 var textNode = node.firstChild;
25278 node.removeChild(textNode);
25279 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25280 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25282 node.parentNode.insertBefore(textNode, node);
25283 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25284 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25286 node.parentNode.removeChild(node);
25289 if (node.nodeName == "#text") {
25290 // clean up silly Windows -- stuff?
25293 if (node.nodeName == "#comment") {
25294 node.parentNode.removeChild(node);
25295 // clean up silly Windows -- stuff?
25299 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25300 node.parentNode.removeChild(node);
25303 //Roo.log(node.tagName);
25304 // remove - but keep children..
25305 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25306 //Roo.log('-- removed');
25307 while (node.childNodes.length) {
25308 var cn = node.childNodes[0];
25309 node.removeChild(cn);
25310 node.parentNode.insertBefore(cn, node);
25311 // move node to parent - and clean it..
25312 this.cleanWord(cn);
25314 node.parentNode.removeChild(node);
25315 /// no need to iterate chidlren = it's got none..
25316 //this.iterateChildren(node, this.cleanWord);
25320 if (node.className.length) {
25322 var cn = node.className.split(/\W+/);
25324 Roo.each(cn, function(cls) {
25325 if (cls.match(/Mso[a-zA-Z]+/)) {
25330 node.className = cna.length ? cna.join(' ') : '';
25332 node.removeAttribute("class");
25336 if (node.hasAttribute("lang")) {
25337 node.removeAttribute("lang");
25340 if (node.hasAttribute("style")) {
25342 var styles = node.getAttribute("style").split(";");
25344 Roo.each(styles, function(s) {
25345 if (!s.match(/:/)) {
25348 var kv = s.split(":");
25349 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25352 // what ever is left... we allow.
25355 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25356 if (!nstyle.length) {
25357 node.removeAttribute('style');
25360 this.iterateChildren(node, this.cleanWord);
25366 * iterateChildren of a Node, calling fn each time, using this as the scole..
25367 * @param {DomNode} node node to iterate children of.
25368 * @param {Function} fn method of this class to call on each item.
25370 iterateChildren : function(node, fn)
25372 if (!node.childNodes.length) {
25375 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25376 fn.call(this, node.childNodes[i])
25382 * cleanTableWidths.
25384 * Quite often pasting from word etc.. results in tables with column and widths.
25385 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25388 cleanTableWidths : function(node)
25393 this.cleanTableWidths(this.doc.body);
25398 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25401 Roo.log(node.tagName);
25402 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25403 this.iterateChildren(node, this.cleanTableWidths);
25406 if (node.hasAttribute('width')) {
25407 node.removeAttribute('width');
25411 if (node.hasAttribute("style")) {
25414 var styles = node.getAttribute("style").split(";");
25416 Roo.each(styles, function(s) {
25417 if (!s.match(/:/)) {
25420 var kv = s.split(":");
25421 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25424 // what ever is left... we allow.
25427 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25428 if (!nstyle.length) {
25429 node.removeAttribute('style');
25433 this.iterateChildren(node, this.cleanTableWidths);
25441 domToHTML : function(currentElement, depth, nopadtext) {
25443 depth = depth || 0;
25444 nopadtext = nopadtext || false;
25446 if (!currentElement) {
25447 return this.domToHTML(this.doc.body);
25450 //Roo.log(currentElement);
25452 var allText = false;
25453 var nodeName = currentElement.nodeName;
25454 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25456 if (nodeName == '#text') {
25458 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25463 if (nodeName != 'BODY') {
25466 // Prints the node tagName, such as <A>, <IMG>, etc
25469 for(i = 0; i < currentElement.attributes.length;i++) {
25471 var aname = currentElement.attributes.item(i).name;
25472 if (!currentElement.attributes.item(i).value.length) {
25475 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25478 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25487 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25490 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25495 // Traverse the tree
25497 var currentElementChild = currentElement.childNodes.item(i);
25498 var allText = true;
25499 var innerHTML = '';
25501 while (currentElementChild) {
25502 // Formatting code (indent the tree so it looks nice on the screen)
25503 var nopad = nopadtext;
25504 if (lastnode == 'SPAN') {
25508 if (currentElementChild.nodeName == '#text') {
25509 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25510 toadd = nopadtext ? toadd : toadd.trim();
25511 if (!nopad && toadd.length > 80) {
25512 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25514 innerHTML += toadd;
25517 currentElementChild = currentElement.childNodes.item(i);
25523 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25525 // Recursively traverse the tree structure of the child node
25526 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25527 lastnode = currentElementChild.nodeName;
25529 currentElementChild=currentElement.childNodes.item(i);
25535 // The remaining code is mostly for formatting the tree
25536 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25541 ret+= "</"+tagName+">";
25547 applyBlacklists : function()
25549 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25550 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25554 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25555 if (b.indexOf(tag) > -1) {
25558 this.white.push(tag);
25562 Roo.each(w, function(tag) {
25563 if (b.indexOf(tag) > -1) {
25566 if (this.white.indexOf(tag) > -1) {
25569 this.white.push(tag);
25574 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25575 if (w.indexOf(tag) > -1) {
25578 this.black.push(tag);
25582 Roo.each(b, function(tag) {
25583 if (w.indexOf(tag) > -1) {
25586 if (this.black.indexOf(tag) > -1) {
25589 this.black.push(tag);
25594 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25595 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25599 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25600 if (b.indexOf(tag) > -1) {
25603 this.cwhite.push(tag);
25607 Roo.each(w, function(tag) {
25608 if (b.indexOf(tag) > -1) {
25611 if (this.cwhite.indexOf(tag) > -1) {
25614 this.cwhite.push(tag);
25619 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25620 if (w.indexOf(tag) > -1) {
25623 this.cblack.push(tag);
25627 Roo.each(b, function(tag) {
25628 if (w.indexOf(tag) > -1) {
25631 if (this.cblack.indexOf(tag) > -1) {
25634 this.cblack.push(tag);
25639 setStylesheets : function(stylesheets)
25641 if(typeof(stylesheets) == 'string'){
25642 Roo.get(this.iframe.contentDocument.head).createChild({
25644 rel : 'stylesheet',
25653 Roo.each(stylesheets, function(s) {
25658 Roo.get(_this.iframe.contentDocument.head).createChild({
25660 rel : 'stylesheet',
25669 removeStylesheets : function()
25673 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25678 setStyle : function(style)
25680 Roo.get(this.iframe.contentDocument.head).createChild({
25689 // hide stuff that is not compatible
25703 * @event specialkey
25707 * @cfg {String} fieldClass @hide
25710 * @cfg {String} focusClass @hide
25713 * @cfg {String} autoCreate @hide
25716 * @cfg {String} inputType @hide
25719 * @cfg {String} invalidClass @hide
25722 * @cfg {String} invalidText @hide
25725 * @cfg {String} msgFx @hide
25728 * @cfg {String} validateOnBlur @hide
25732 Roo.HtmlEditorCore.white = [
25733 'area', 'br', 'img', 'input', 'hr', 'wbr',
25735 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25736 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25737 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25738 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25739 'table', 'ul', 'xmp',
25741 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25744 'dir', 'menu', 'ol', 'ul', 'dl',
25750 Roo.HtmlEditorCore.black = [
25751 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25753 'base', 'basefont', 'bgsound', 'blink', 'body',
25754 'frame', 'frameset', 'head', 'html', 'ilayer',
25755 'iframe', 'layer', 'link', 'meta', 'object',
25756 'script', 'style' ,'title', 'xml' // clean later..
25758 Roo.HtmlEditorCore.clean = [
25759 'script', 'style', 'title', 'xml'
25761 Roo.HtmlEditorCore.remove = [
25766 Roo.HtmlEditorCore.ablack = [
25770 Roo.HtmlEditorCore.aclean = [
25771 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25775 Roo.HtmlEditorCore.pwhite= [
25776 'http', 'https', 'mailto'
25779 // white listed style attributes.
25780 Roo.HtmlEditorCore.cwhite= [
25781 // 'text-align', /// default is to allow most things..
25787 // black listed style attributes.
25788 Roo.HtmlEditorCore.cblack= [
25789 // 'font-size' -- this can be set by the project
25793 Roo.HtmlEditorCore.swapCodes =[
25812 * @class Roo.bootstrap.HtmlEditor
25813 * @extends Roo.bootstrap.TextArea
25814 * Bootstrap HtmlEditor class
25817 * Create a new HtmlEditor
25818 * @param {Object} config The config object
25821 Roo.bootstrap.HtmlEditor = function(config){
25822 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25823 if (!this.toolbars) {
25824 this.toolbars = [];
25827 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25830 * @event initialize
25831 * Fires when the editor is fully initialized (including the iframe)
25832 * @param {HtmlEditor} this
25837 * Fires when the editor is first receives the focus. Any insertion must wait
25838 * until after this event.
25839 * @param {HtmlEditor} this
25843 * @event beforesync
25844 * Fires before the textarea is updated with content from the editor iframe. Return false
25845 * to cancel the sync.
25846 * @param {HtmlEditor} this
25847 * @param {String} html
25851 * @event beforepush
25852 * Fires before the iframe editor is updated with content from the textarea. Return false
25853 * to cancel the push.
25854 * @param {HtmlEditor} this
25855 * @param {String} html
25860 * Fires when the textarea is updated with content from the editor iframe.
25861 * @param {HtmlEditor} this
25862 * @param {String} html
25867 * Fires when the iframe editor is updated with content from the textarea.
25868 * @param {HtmlEditor} this
25869 * @param {String} html
25873 * @event editmodechange
25874 * Fires when the editor switches edit modes
25875 * @param {HtmlEditor} this
25876 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25878 editmodechange: true,
25880 * @event editorevent
25881 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25882 * @param {HtmlEditor} this
25886 * @event firstfocus
25887 * Fires when on first focus - needed by toolbars..
25888 * @param {HtmlEditor} this
25893 * Auto save the htmlEditor value as a file into Events
25894 * @param {HtmlEditor} this
25898 * @event savedpreview
25899 * preview the saved version of htmlEditor
25900 * @param {HtmlEditor} this
25907 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25911 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25916 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25921 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25926 * @cfg {Number} height (in pixels)
25930 * @cfg {Number} width (in pixels)
25935 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25938 stylesheets: false,
25943 // private properties
25944 validationEvent : false,
25946 initialized : false,
25949 onFocus : Roo.emptyFn,
25951 hideMode:'offsets',
25953 tbContainer : false,
25957 toolbarContainer :function() {
25958 return this.wrap.select('.x-html-editor-tb',true).first();
25962 * Protected method that will not generally be called directly. It
25963 * is called when the editor creates its toolbar. Override this method if you need to
25964 * add custom toolbar buttons.
25965 * @param {HtmlEditor} editor
25967 createToolbar : function(){
25968 Roo.log('renewing');
25969 Roo.log("create toolbars");
25971 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25972 this.toolbars[0].render(this.toolbarContainer());
25976 // if (!editor.toolbars || !editor.toolbars.length) {
25977 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25980 // for (var i =0 ; i < editor.toolbars.length;i++) {
25981 // editor.toolbars[i] = Roo.factory(
25982 // typeof(editor.toolbars[i]) == 'string' ?
25983 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25984 // Roo.bootstrap.HtmlEditor);
25985 // editor.toolbars[i].init(editor);
25991 onRender : function(ct, position)
25993 // Roo.log("Call onRender: " + this.xtype);
25995 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25997 this.wrap = this.inputEl().wrap({
25998 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26001 this.editorcore.onRender(ct, position);
26003 if (this.resizable) {
26004 this.resizeEl = new Roo.Resizable(this.wrap, {
26008 minHeight : this.height,
26009 height: this.height,
26010 handles : this.resizable,
26013 resize : function(r, w, h) {
26014 _t.onResize(w,h); // -something
26020 this.createToolbar(this);
26023 if(!this.width && this.resizable){
26024 this.setSize(this.wrap.getSize());
26026 if (this.resizeEl) {
26027 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26028 // should trigger onReize..
26034 onResize : function(w, h)
26036 Roo.log('resize: ' +w + ',' + h );
26037 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26041 if(this.inputEl() ){
26042 if(typeof w == 'number'){
26043 var aw = w - this.wrap.getFrameWidth('lr');
26044 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26047 if(typeof h == 'number'){
26048 var tbh = -11; // fixme it needs to tool bar size!
26049 for (var i =0; i < this.toolbars.length;i++) {
26050 // fixme - ask toolbars for heights?
26051 tbh += this.toolbars[i].el.getHeight();
26052 //if (this.toolbars[i].footer) {
26053 // tbh += this.toolbars[i].footer.el.getHeight();
26061 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26062 ah -= 5; // knock a few pixes off for look..
26063 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26067 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26068 this.editorcore.onResize(ew,eh);
26073 * Toggles the editor between standard and source edit mode.
26074 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26076 toggleSourceEdit : function(sourceEditMode)
26078 this.editorcore.toggleSourceEdit(sourceEditMode);
26080 if(this.editorcore.sourceEditMode){
26081 Roo.log('editor - showing textarea');
26084 // Roo.log(this.syncValue());
26086 this.inputEl().removeClass(['hide', 'x-hidden']);
26087 this.inputEl().dom.removeAttribute('tabIndex');
26088 this.inputEl().focus();
26090 Roo.log('editor - hiding textarea');
26092 // Roo.log(this.pushValue());
26095 this.inputEl().addClass(['hide', 'x-hidden']);
26096 this.inputEl().dom.setAttribute('tabIndex', -1);
26097 //this.deferFocus();
26100 if(this.resizable){
26101 this.setSize(this.wrap.getSize());
26104 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26107 // private (for BoxComponent)
26108 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26110 // private (for BoxComponent)
26111 getResizeEl : function(){
26115 // private (for BoxComponent)
26116 getPositionEl : function(){
26121 initEvents : function(){
26122 this.originalValue = this.getValue();
26126 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26129 // markInvalid : Roo.emptyFn,
26131 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26134 // clearInvalid : Roo.emptyFn,
26136 setValue : function(v){
26137 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26138 this.editorcore.pushValue();
26143 deferFocus : function(){
26144 this.focus.defer(10, this);
26148 focus : function(){
26149 this.editorcore.focus();
26155 onDestroy : function(){
26161 for (var i =0; i < this.toolbars.length;i++) {
26162 // fixme - ask toolbars for heights?
26163 this.toolbars[i].onDestroy();
26166 this.wrap.dom.innerHTML = '';
26167 this.wrap.remove();
26172 onFirstFocus : function(){
26173 //Roo.log("onFirstFocus");
26174 this.editorcore.onFirstFocus();
26175 for (var i =0; i < this.toolbars.length;i++) {
26176 this.toolbars[i].onFirstFocus();
26182 syncValue : function()
26184 this.editorcore.syncValue();
26187 pushValue : function()
26189 this.editorcore.pushValue();
26193 // hide stuff that is not compatible
26207 * @event specialkey
26211 * @cfg {String} fieldClass @hide
26214 * @cfg {String} focusClass @hide
26217 * @cfg {String} autoCreate @hide
26220 * @cfg {String} inputType @hide
26224 * @cfg {String} invalidText @hide
26227 * @cfg {String} msgFx @hide
26230 * @cfg {String} validateOnBlur @hide
26239 Roo.namespace('Roo.bootstrap.htmleditor');
26241 * @class Roo.bootstrap.HtmlEditorToolbar1
26247 new Roo.bootstrap.HtmlEditor({
26250 new Roo.bootstrap.HtmlEditorToolbar1({
26251 disable : { fonts: 1 , format: 1, ..., ... , ...],
26257 * @cfg {Object} disable List of elements to disable..
26258 * @cfg {Array} btns List of additional buttons.
26262 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26265 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26268 Roo.apply(this, config);
26270 // default disabled, based on 'good practice'..
26271 this.disable = this.disable || {};
26272 Roo.applyIf(this.disable, {
26275 specialElements : true
26277 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26279 this.editor = config.editor;
26280 this.editorcore = config.editor.editorcore;
26282 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26284 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26285 // dont call parent... till later.
26287 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26292 editorcore : false,
26297 "h1","h2","h3","h4","h5","h6",
26299 "abbr", "acronym", "address", "cite", "samp", "var",
26303 onRender : function(ct, position)
26305 // Roo.log("Call onRender: " + this.xtype);
26307 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26309 this.el.dom.style.marginBottom = '0';
26311 var editorcore = this.editorcore;
26312 var editor= this.editor;
26315 var btn = function(id,cmd , toggle, handler, html){
26317 var event = toggle ? 'toggle' : 'click';
26322 xns: Roo.bootstrap,
26326 enableToggle:toggle !== false,
26328 pressed : toggle ? false : null,
26331 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26332 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26338 // var cb_box = function...
26343 xns: Roo.bootstrap,
26348 xns: Roo.bootstrap,
26352 Roo.each(this.formats, function(f) {
26353 style.menu.items.push({
26355 xns: Roo.bootstrap,
26356 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26361 editorcore.insertTag(this.tagname);
26368 children.push(style);
26370 btn('bold',false,true);
26371 btn('italic',false,true);
26372 btn('align-left', 'justifyleft',true);
26373 btn('align-center', 'justifycenter',true);
26374 btn('align-right' , 'justifyright',true);
26375 btn('link', false, false, function(btn) {
26376 //Roo.log("create link?");
26377 var url = prompt(this.createLinkText, this.defaultLinkValue);
26378 if(url && url != 'http:/'+'/'){
26379 this.editorcore.relayCmd('createlink', url);
26382 btn('list','insertunorderedlist',true);
26383 btn('pencil', false,true, function(btn){
26385 this.toggleSourceEdit(btn.pressed);
26388 if (this.editor.btns.length > 0) {
26389 for (var i = 0; i<this.editor.btns.length; i++) {
26390 children.push(this.editor.btns[i]);
26398 xns: Roo.bootstrap,
26403 xns: Roo.bootstrap,
26408 cog.menu.items.push({
26410 xns: Roo.bootstrap,
26411 html : Clean styles,
26416 editorcore.insertTag(this.tagname);
26425 this.xtype = 'NavSimplebar';
26427 for(var i=0;i< children.length;i++) {
26429 this.buttons.add(this.addxtypeChild(children[i]));
26433 editor.on('editorevent', this.updateToolbar, this);
26435 onBtnClick : function(id)
26437 this.editorcore.relayCmd(id);
26438 this.editorcore.focus();
26442 * Protected method that will not generally be called directly. It triggers
26443 * a toolbar update by reading the markup state of the current selection in the editor.
26445 updateToolbar: function(){
26447 if(!this.editorcore.activated){
26448 this.editor.onFirstFocus(); // is this neeed?
26452 var btns = this.buttons;
26453 var doc = this.editorcore.doc;
26454 btns.get('bold').setActive(doc.queryCommandState('bold'));
26455 btns.get('italic').setActive(doc.queryCommandState('italic'));
26456 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26458 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26459 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26460 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26462 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26463 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26466 var ans = this.editorcore.getAllAncestors();
26467 if (this.formatCombo) {
26470 var store = this.formatCombo.store;
26471 this.formatCombo.setValue("");
26472 for (var i =0; i < ans.length;i++) {
26473 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26475 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26483 // hides menus... - so this cant be on a menu...
26484 Roo.bootstrap.MenuMgr.hideAll();
26486 Roo.bootstrap.MenuMgr.hideAll();
26487 //this.editorsyncValue();
26489 onFirstFocus: function() {
26490 this.buttons.each(function(item){
26494 toggleSourceEdit : function(sourceEditMode){
26497 if(sourceEditMode){
26498 Roo.log("disabling buttons");
26499 this.buttons.each( function(item){
26500 if(item.cmd != 'pencil'){
26506 Roo.log("enabling buttons");
26507 if(this.editorcore.initialized){
26508 this.buttons.each( function(item){
26514 Roo.log("calling toggole on editor");
26515 // tell the editor that it's been pressed..
26516 this.editor.toggleSourceEdit(sourceEditMode);
26530 * @class Roo.bootstrap.Markdown
26531 * @extends Roo.bootstrap.TextArea
26532 * Bootstrap Showdown editable area
26533 * @cfg {string} content
26536 * Create a new Showdown
26539 Roo.bootstrap.Markdown = function(config){
26540 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26544 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26548 initEvents : function()
26551 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26552 this.markdownEl = this.el.createChild({
26553 cls : 'roo-markdown-area'
26555 this.inputEl().addClass('d-none');
26556 if (this.getValue() == '') {
26557 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26560 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26562 this.markdownEl.on('click', this.toggleTextEdit, this);
26563 this.on('blur', this.toggleTextEdit, this);
26564 this.on('specialkey', this.resizeTextArea, this);
26567 toggleTextEdit : function()
26569 var sh = this.markdownEl.getHeight();
26570 this.inputEl().addClass('d-none');
26571 this.markdownEl.addClass('d-none');
26572 if (!this.editing) {
26574 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26575 this.inputEl().removeClass('d-none');
26576 this.inputEl().focus();
26577 this.editing = true;
26580 // show showdown...
26581 this.updateMarkdown();
26582 this.markdownEl.removeClass('d-none');
26583 this.editing = false;
26586 updateMarkdown : function()
26588 if (this.getValue() == '') {
26589 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26593 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26596 resizeTextArea: function () {
26599 Roo.log([sh, this.getValue().split("\n").length * 30]);
26600 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26602 setValue : function(val)
26604 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26605 if (!this.editing) {
26606 this.updateMarkdown();
26612 if (!this.editing) {
26613 this.toggleTextEdit();
26621 * @class Roo.bootstrap.Table.AbstractSelectionModel
26622 * @extends Roo.util.Observable
26623 * Abstract base class for grid SelectionModels. It provides the interface that should be
26624 * implemented by descendant classes. This class should not be directly instantiated.
26627 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26628 this.locked = false;
26629 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26633 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26634 /** @ignore Called by the grid automatically. Do not call directly. */
26635 init : function(grid){
26641 * Locks the selections.
26644 this.locked = true;
26648 * Unlocks the selections.
26650 unlock : function(){
26651 this.locked = false;
26655 * Returns true if the selections are locked.
26656 * @return {Boolean}
26658 isLocked : function(){
26659 return this.locked;
26663 initEvents : function ()
26669 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26670 * @class Roo.bootstrap.Table.RowSelectionModel
26671 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26672 * It supports multiple selections and keyboard selection/navigation.
26674 * @param {Object} config
26677 Roo.bootstrap.Table.RowSelectionModel = function(config){
26678 Roo.apply(this, config);
26679 this.selections = new Roo.util.MixedCollection(false, function(o){
26684 this.lastActive = false;
26688 * @event selectionchange
26689 * Fires when the selection changes
26690 * @param {SelectionModel} this
26692 "selectionchange" : true,
26694 * @event afterselectionchange
26695 * Fires after the selection changes (eg. by key press or clicking)
26696 * @param {SelectionModel} this
26698 "afterselectionchange" : true,
26700 * @event beforerowselect
26701 * Fires when a row is selected being selected, return false to cancel.
26702 * @param {SelectionModel} this
26703 * @param {Number} rowIndex The selected index
26704 * @param {Boolean} keepExisting False if other selections will be cleared
26706 "beforerowselect" : true,
26709 * Fires when a row is selected.
26710 * @param {SelectionModel} this
26711 * @param {Number} rowIndex The selected index
26712 * @param {Roo.data.Record} r The record
26714 "rowselect" : true,
26716 * @event rowdeselect
26717 * Fires when a row is deselected.
26718 * @param {SelectionModel} this
26719 * @param {Number} rowIndex The selected index
26721 "rowdeselect" : true
26723 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26724 this.locked = false;
26727 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26729 * @cfg {Boolean} singleSelect
26730 * True to allow selection of only one row at a time (defaults to false)
26732 singleSelect : false,
26735 initEvents : function()
26738 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26739 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26740 //}else{ // allow click to work like normal
26741 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26743 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26744 this.grid.on("rowclick", this.handleMouseDown, this);
26746 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26747 "up" : function(e){
26749 this.selectPrevious(e.shiftKey);
26750 }else if(this.last !== false && this.lastActive !== false){
26751 var last = this.last;
26752 this.selectRange(this.last, this.lastActive-1);
26753 this.grid.getView().focusRow(this.lastActive);
26754 if(last !== false){
26758 this.selectFirstRow();
26760 this.fireEvent("afterselectionchange", this);
26762 "down" : function(e){
26764 this.selectNext(e.shiftKey);
26765 }else if(this.last !== false && this.lastActive !== false){
26766 var last = this.last;
26767 this.selectRange(this.last, this.lastActive+1);
26768 this.grid.getView().focusRow(this.lastActive);
26769 if(last !== false){
26773 this.selectFirstRow();
26775 this.fireEvent("afterselectionchange", this);
26779 this.grid.store.on('load', function(){
26780 this.selections.clear();
26783 var view = this.grid.view;
26784 view.on("refresh", this.onRefresh, this);
26785 view.on("rowupdated", this.onRowUpdated, this);
26786 view.on("rowremoved", this.onRemove, this);
26791 onRefresh : function()
26793 var ds = this.grid.store, i, v = this.grid.view;
26794 var s = this.selections;
26795 s.each(function(r){
26796 if((i = ds.indexOfId(r.id)) != -1){
26805 onRemove : function(v, index, r){
26806 this.selections.remove(r);
26810 onRowUpdated : function(v, index, r){
26811 if(this.isSelected(r)){
26812 v.onRowSelect(index);
26818 * @param {Array} records The records to select
26819 * @param {Boolean} keepExisting (optional) True to keep existing selections
26821 selectRecords : function(records, keepExisting)
26824 this.clearSelections();
26826 var ds = this.grid.store;
26827 for(var i = 0, len = records.length; i < len; i++){
26828 this.selectRow(ds.indexOf(records[i]), true);
26833 * Gets the number of selected rows.
26836 getCount : function(){
26837 return this.selections.length;
26841 * Selects the first row in the grid.
26843 selectFirstRow : function(){
26848 * Select the last row.
26849 * @param {Boolean} keepExisting (optional) True to keep existing selections
26851 selectLastRow : function(keepExisting){
26852 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26853 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26857 * Selects the row immediately following the last selected row.
26858 * @param {Boolean} keepExisting (optional) True to keep existing selections
26860 selectNext : function(keepExisting)
26862 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26863 this.selectRow(this.last+1, keepExisting);
26864 this.grid.getView().focusRow(this.last);
26869 * Selects the row that precedes the last selected row.
26870 * @param {Boolean} keepExisting (optional) True to keep existing selections
26872 selectPrevious : function(keepExisting){
26874 this.selectRow(this.last-1, keepExisting);
26875 this.grid.getView().focusRow(this.last);
26880 * Returns the selected records
26881 * @return {Array} Array of selected records
26883 getSelections : function(){
26884 return [].concat(this.selections.items);
26888 * Returns the first selected record.
26891 getSelected : function(){
26892 return this.selections.itemAt(0);
26897 * Clears all selections.
26899 clearSelections : function(fast)
26905 var ds = this.grid.store;
26906 var s = this.selections;
26907 s.each(function(r){
26908 this.deselectRow(ds.indexOfId(r.id));
26912 this.selections.clear();
26919 * Selects all rows.
26921 selectAll : function(){
26925 this.selections.clear();
26926 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26927 this.selectRow(i, true);
26932 * Returns True if there is a selection.
26933 * @return {Boolean}
26935 hasSelection : function(){
26936 return this.selections.length > 0;
26940 * Returns True if the specified row is selected.
26941 * @param {Number/Record} record The record or index of the record to check
26942 * @return {Boolean}
26944 isSelected : function(index){
26945 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26946 return (r && this.selections.key(r.id) ? true : false);
26950 * Returns True if the specified record id is selected.
26951 * @param {String} id The id of record to check
26952 * @return {Boolean}
26954 isIdSelected : function(id){
26955 return (this.selections.key(id) ? true : false);
26960 handleMouseDBClick : function(e, t){
26964 handleMouseDown : function(e, t)
26966 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26967 if(this.isLocked() || rowIndex < 0 ){
26970 if(e.shiftKey && this.last !== false){
26971 var last = this.last;
26972 this.selectRange(last, rowIndex, e.ctrlKey);
26973 this.last = last; // reset the last
26977 var isSelected = this.isSelected(rowIndex);
26978 //Roo.log("select row:" + rowIndex);
26980 this.deselectRow(rowIndex);
26982 this.selectRow(rowIndex, true);
26986 if(e.button !== 0 && isSelected){
26987 alert('rowIndex 2: ' + rowIndex);
26988 view.focusRow(rowIndex);
26989 }else if(e.ctrlKey && isSelected){
26990 this.deselectRow(rowIndex);
26991 }else if(!isSelected){
26992 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26993 view.focusRow(rowIndex);
26997 this.fireEvent("afterselectionchange", this);
27000 handleDragableRowClick : function(grid, rowIndex, e)
27002 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27003 this.selectRow(rowIndex, false);
27004 grid.view.focusRow(rowIndex);
27005 this.fireEvent("afterselectionchange", this);
27010 * Selects multiple rows.
27011 * @param {Array} rows Array of the indexes of the row to select
27012 * @param {Boolean} keepExisting (optional) True to keep existing selections
27014 selectRows : function(rows, keepExisting){
27016 this.clearSelections();
27018 for(var i = 0, len = rows.length; i < len; i++){
27019 this.selectRow(rows[i], true);
27024 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27025 * @param {Number} startRow The index of the first row in the range
27026 * @param {Number} endRow The index of the last row in the range
27027 * @param {Boolean} keepExisting (optional) True to retain existing selections
27029 selectRange : function(startRow, endRow, keepExisting){
27034 this.clearSelections();
27036 if(startRow <= endRow){
27037 for(var i = startRow; i <= endRow; i++){
27038 this.selectRow(i, true);
27041 for(var i = startRow; i >= endRow; i--){
27042 this.selectRow(i, true);
27048 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27049 * @param {Number} startRow The index of the first row in the range
27050 * @param {Number} endRow The index of the last row in the range
27052 deselectRange : function(startRow, endRow, preventViewNotify){
27056 for(var i = startRow; i <= endRow; i++){
27057 this.deselectRow(i, preventViewNotify);
27063 * @param {Number} row The index of the row to select
27064 * @param {Boolean} keepExisting (optional) True to keep existing selections
27066 selectRow : function(index, keepExisting, preventViewNotify)
27068 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27071 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27072 if(!keepExisting || this.singleSelect){
27073 this.clearSelections();
27076 var r = this.grid.store.getAt(index);
27077 //console.log('selectRow - record id :' + r.id);
27079 this.selections.add(r);
27080 this.last = this.lastActive = index;
27081 if(!preventViewNotify){
27082 var proxy = new Roo.Element(
27083 this.grid.getRowDom(index)
27085 proxy.addClass('bg-info info');
27087 this.fireEvent("rowselect", this, index, r);
27088 this.fireEvent("selectionchange", this);
27094 * @param {Number} row The index of the row to deselect
27096 deselectRow : function(index, preventViewNotify)
27101 if(this.last == index){
27104 if(this.lastActive == index){
27105 this.lastActive = false;
27108 var r = this.grid.store.getAt(index);
27113 this.selections.remove(r);
27114 //.console.log('deselectRow - record id :' + r.id);
27115 if(!preventViewNotify){
27117 var proxy = new Roo.Element(
27118 this.grid.getRowDom(index)
27120 proxy.removeClass('bg-info info');
27122 this.fireEvent("rowdeselect", this, index);
27123 this.fireEvent("selectionchange", this);
27127 restoreLast : function(){
27129 this.last = this._last;
27134 acceptsNav : function(row, col, cm){
27135 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27139 onEditorKey : function(field, e){
27140 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27145 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27147 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27149 }else if(k == e.ENTER && !e.ctrlKey){
27153 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27155 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27157 }else if(k == e.ESC){
27161 g.startEditing(newCell[0], newCell[1]);
27167 * Ext JS Library 1.1.1
27168 * Copyright(c) 2006-2007, Ext JS, LLC.
27170 * Originally Released Under LGPL - original licence link has changed is not relivant.
27173 * <script type="text/javascript">
27177 * @class Roo.bootstrap.PagingToolbar
27178 * @extends Roo.bootstrap.NavSimplebar
27179 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27181 * Create a new PagingToolbar
27182 * @param {Object} config The config object
27183 * @param {Roo.data.Store} store
27185 Roo.bootstrap.PagingToolbar = function(config)
27187 // old args format still supported... - xtype is prefered..
27188 // created from xtype...
27190 this.ds = config.dataSource;
27192 if (config.store && !this.ds) {
27193 this.store= Roo.factory(config.store, Roo.data);
27194 this.ds = this.store;
27195 this.ds.xmodule = this.xmodule || false;
27198 this.toolbarItems = [];
27199 if (config.items) {
27200 this.toolbarItems = config.items;
27203 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27208 this.bind(this.ds);
27211 if (Roo.bootstrap.version == 4) {
27212 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27214 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27219 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27221 * @cfg {Roo.data.Store} dataSource
27222 * The underlying data store providing the paged data
27225 * @cfg {String/HTMLElement/Element} container
27226 * container The id or element that will contain the toolbar
27229 * @cfg {Boolean} displayInfo
27230 * True to display the displayMsg (defaults to false)
27233 * @cfg {Number} pageSize
27234 * The number of records to display per page (defaults to 20)
27238 * @cfg {String} displayMsg
27239 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27241 displayMsg : 'Displaying {0} - {1} of {2}',
27243 * @cfg {String} emptyMsg
27244 * The message to display when no records are found (defaults to "No data to display")
27246 emptyMsg : 'No data to display',
27248 * Customizable piece of the default paging text (defaults to "Page")
27251 beforePageText : "Page",
27253 * Customizable piece of the default paging text (defaults to "of %0")
27256 afterPageText : "of {0}",
27258 * Customizable piece of the default paging text (defaults to "First Page")
27261 firstText : "First Page",
27263 * Customizable piece of the default paging text (defaults to "Previous Page")
27266 prevText : "Previous Page",
27268 * Customizable piece of the default paging text (defaults to "Next Page")
27271 nextText : "Next Page",
27273 * Customizable piece of the default paging text (defaults to "Last Page")
27276 lastText : "Last Page",
27278 * Customizable piece of the default paging text (defaults to "Refresh")
27281 refreshText : "Refresh",
27285 onRender : function(ct, position)
27287 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27288 this.navgroup.parentId = this.id;
27289 this.navgroup.onRender(this.el, null);
27290 // add the buttons to the navgroup
27292 if(this.displayInfo){
27293 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27294 this.displayEl = this.el.select('.x-paging-info', true).first();
27295 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27296 // this.displayEl = navel.el.select('span',true).first();
27302 Roo.each(_this.buttons, function(e){ // this might need to use render????
27303 Roo.factory(e).render(_this.el);
27307 Roo.each(_this.toolbarItems, function(e) {
27308 _this.navgroup.addItem(e);
27312 this.first = this.navgroup.addItem({
27313 tooltip: this.firstText,
27314 cls: "prev btn-outline-secondary",
27315 html : ' <i class="fa fa-step-backward"></i>',
27317 preventDefault: true,
27318 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27321 this.prev = this.navgroup.addItem({
27322 tooltip: this.prevText,
27323 cls: "prev btn-outline-secondary",
27324 html : ' <i class="fa fa-backward"></i>',
27326 preventDefault: true,
27327 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27329 //this.addSeparator();
27332 var field = this.navgroup.addItem( {
27334 cls : 'x-paging-position btn-outline-secondary',
27336 html : this.beforePageText +
27337 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27338 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27341 this.field = field.el.select('input', true).first();
27342 this.field.on("keydown", this.onPagingKeydown, this);
27343 this.field.on("focus", function(){this.dom.select();});
27346 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27347 //this.field.setHeight(18);
27348 //this.addSeparator();
27349 this.next = this.navgroup.addItem({
27350 tooltip: this.nextText,
27351 cls: "next btn-outline-secondary",
27352 html : ' <i class="fa fa-forward"></i>',
27354 preventDefault: true,
27355 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27357 this.last = this.navgroup.addItem({
27358 tooltip: this.lastText,
27359 html : ' <i class="fa fa-step-forward"></i>',
27360 cls: "next btn-outline-secondary",
27362 preventDefault: true,
27363 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27365 //this.addSeparator();
27366 this.loading = this.navgroup.addItem({
27367 tooltip: this.refreshText,
27368 cls: "btn-outline-secondary",
27369 html : ' <i class="fa fa-refresh"></i>',
27370 preventDefault: true,
27371 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27377 updateInfo : function(){
27378 if(this.displayEl){
27379 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27380 var msg = count == 0 ?
27384 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27386 this.displayEl.update(msg);
27391 onLoad : function(ds, r, o)
27393 this.cursor = o.params && o.params.start ? o.params.start : 0;
27395 var d = this.getPageData(),
27400 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27401 this.field.dom.value = ap;
27402 this.first.setDisabled(ap == 1);
27403 this.prev.setDisabled(ap == 1);
27404 this.next.setDisabled(ap == ps);
27405 this.last.setDisabled(ap == ps);
27406 this.loading.enable();
27411 getPageData : function(){
27412 var total = this.ds.getTotalCount();
27415 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27416 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27421 onLoadError : function(){
27422 this.loading.enable();
27426 onPagingKeydown : function(e){
27427 var k = e.getKey();
27428 var d = this.getPageData();
27430 var v = this.field.dom.value, pageNum;
27431 if(!v || isNaN(pageNum = parseInt(v, 10))){
27432 this.field.dom.value = d.activePage;
27435 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27436 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27439 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))
27441 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27442 this.field.dom.value = pageNum;
27443 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27446 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27448 var v = this.field.dom.value, pageNum;
27449 var increment = (e.shiftKey) ? 10 : 1;
27450 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27453 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27454 this.field.dom.value = d.activePage;
27457 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27459 this.field.dom.value = parseInt(v, 10) + increment;
27460 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27461 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27468 beforeLoad : function(){
27470 this.loading.disable();
27475 onClick : function(which){
27484 ds.load({params:{start: 0, limit: this.pageSize}});
27487 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27490 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27493 var total = ds.getTotalCount();
27494 var extra = total % this.pageSize;
27495 var lastStart = extra ? (total - extra) : total-this.pageSize;
27496 ds.load({params:{start: lastStart, limit: this.pageSize}});
27499 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27505 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27506 * @param {Roo.data.Store} store The data store to unbind
27508 unbind : function(ds){
27509 ds.un("beforeload", this.beforeLoad, this);
27510 ds.un("load", this.onLoad, this);
27511 ds.un("loadexception", this.onLoadError, this);
27512 ds.un("remove", this.updateInfo, this);
27513 ds.un("add", this.updateInfo, this);
27514 this.ds = undefined;
27518 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27519 * @param {Roo.data.Store} store The data store to bind
27521 bind : function(ds){
27522 ds.on("beforeload", this.beforeLoad, this);
27523 ds.on("load", this.onLoad, this);
27524 ds.on("loadexception", this.onLoadError, this);
27525 ds.on("remove", this.updateInfo, this);
27526 ds.on("add", this.updateInfo, this);
27537 * @class Roo.bootstrap.MessageBar
27538 * @extends Roo.bootstrap.Component
27539 * Bootstrap MessageBar class
27540 * @cfg {String} html contents of the MessageBar
27541 * @cfg {String} weight (info | success | warning | danger) default info
27542 * @cfg {String} beforeClass insert the bar before the given class
27543 * @cfg {Boolean} closable (true | false) default false
27544 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27547 * Create a new Element
27548 * @param {Object} config The config object
27551 Roo.bootstrap.MessageBar = function(config){
27552 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27555 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27561 beforeClass: 'bootstrap-sticky-wrap',
27563 getAutoCreate : function(){
27567 cls: 'alert alert-dismissable alert-' + this.weight,
27572 html: this.html || ''
27578 cfg.cls += ' alert-messages-fixed';
27592 onRender : function(ct, position)
27594 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27597 var cfg = Roo.apply({}, this.getAutoCreate());
27601 cfg.cls += ' ' + this.cls;
27604 cfg.style = this.style;
27606 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27608 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27611 this.el.select('>button.close').on('click', this.hide, this);
27617 if (!this.rendered) {
27623 this.fireEvent('show', this);
27629 if (!this.rendered) {
27635 this.fireEvent('hide', this);
27638 update : function()
27640 // var e = this.el.dom.firstChild;
27642 // if(this.closable){
27643 // e = e.nextSibling;
27646 // e.data = this.html || '';
27648 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27664 * @class Roo.bootstrap.Graph
27665 * @extends Roo.bootstrap.Component
27666 * Bootstrap Graph class
27670 @cfg {String} graphtype bar | vbar | pie
27671 @cfg {number} g_x coodinator | centre x (pie)
27672 @cfg {number} g_y coodinator | centre y (pie)
27673 @cfg {number} g_r radius (pie)
27674 @cfg {number} g_height height of the chart (respected by all elements in the set)
27675 @cfg {number} g_width width of the chart (respected by all elements in the set)
27676 @cfg {Object} title The title of the chart
27679 -opts (object) options for the chart
27681 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27682 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27684 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.
27685 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27687 o stretch (boolean)
27689 -opts (object) options for the pie
27692 o startAngle (number)
27693 o endAngle (number)
27697 * Create a new Input
27698 * @param {Object} config The config object
27701 Roo.bootstrap.Graph = function(config){
27702 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27708 * The img click event for the img.
27709 * @param {Roo.EventObject} e
27715 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27726 //g_colors: this.colors,
27733 getAutoCreate : function(){
27744 onRender : function(ct,position){
27747 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27749 if (typeof(Raphael) == 'undefined') {
27750 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27754 this.raphael = Raphael(this.el.dom);
27756 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27757 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27758 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27759 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27761 r.text(160, 10, "Single Series Chart").attr(txtattr);
27762 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27763 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27764 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27766 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27767 r.barchart(330, 10, 300, 220, data1);
27768 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27769 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27772 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27773 // r.barchart(30, 30, 560, 250, xdata, {
27774 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27775 // axis : "0 0 1 1",
27776 // axisxlabels : xdata
27777 // //yvalues : cols,
27780 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27782 // this.load(null,xdata,{
27783 // axis : "0 0 1 1",
27784 // axisxlabels : xdata
27789 load : function(graphtype,xdata,opts)
27791 this.raphael.clear();
27793 graphtype = this.graphtype;
27798 var r = this.raphael,
27799 fin = function () {
27800 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27802 fout = function () {
27803 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27805 pfin = function() {
27806 this.sector.stop();
27807 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27810 this.label[0].stop();
27811 this.label[0].attr({ r: 7.5 });
27812 this.label[1].attr({ "font-weight": 800 });
27815 pfout = function() {
27816 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27819 this.label[0].animate({ r: 5 }, 500, "bounce");
27820 this.label[1].attr({ "font-weight": 400 });
27826 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27829 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27832 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27833 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27835 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27842 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27847 setTitle: function(o)
27852 initEvents: function() {
27855 this.el.on('click', this.onClick, this);
27859 onClick : function(e)
27861 Roo.log('img onclick');
27862 this.fireEvent('click', this, e);
27874 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27877 * @class Roo.bootstrap.dash.NumberBox
27878 * @extends Roo.bootstrap.Component
27879 * Bootstrap NumberBox class
27880 * @cfg {String} headline Box headline
27881 * @cfg {String} content Box content
27882 * @cfg {String} icon Box icon
27883 * @cfg {String} footer Footer text
27884 * @cfg {String} fhref Footer href
27887 * Create a new NumberBox
27888 * @param {Object} config The config object
27892 Roo.bootstrap.dash.NumberBox = function(config){
27893 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27897 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27906 getAutoCreate : function(){
27910 cls : 'small-box ',
27918 cls : 'roo-headline',
27919 html : this.headline
27923 cls : 'roo-content',
27924 html : this.content
27938 cls : 'ion ' + this.icon
27947 cls : 'small-box-footer',
27948 href : this.fhref || '#',
27952 cfg.cn.push(footer);
27959 onRender : function(ct,position){
27960 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27967 setHeadline: function (value)
27969 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27972 setFooter: function (value, href)
27974 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27977 this.el.select('a.small-box-footer',true).first().attr('href', href);
27982 setContent: function (value)
27984 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27987 initEvents: function()
28001 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28004 * @class Roo.bootstrap.dash.TabBox
28005 * @extends Roo.bootstrap.Component
28006 * Bootstrap TabBox class
28007 * @cfg {String} title Title of the TabBox
28008 * @cfg {String} icon Icon of the TabBox
28009 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28010 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28013 * Create a new TabBox
28014 * @param {Object} config The config object
28018 Roo.bootstrap.dash.TabBox = function(config){
28019 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28024 * When a pane is added
28025 * @param {Roo.bootstrap.dash.TabPane} pane
28029 * @event activatepane
28030 * When a pane is activated
28031 * @param {Roo.bootstrap.dash.TabPane} pane
28033 "activatepane" : true
28041 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28046 tabScrollable : false,
28048 getChildContainer : function()
28050 return this.el.select('.tab-content', true).first();
28053 getAutoCreate : function(){
28057 cls: 'pull-left header',
28065 cls: 'fa ' + this.icon
28071 cls: 'nav nav-tabs pull-right',
28077 if(this.tabScrollable){
28084 cls: 'nav nav-tabs pull-right',
28095 cls: 'nav-tabs-custom',
28100 cls: 'tab-content no-padding',
28108 initEvents : function()
28110 //Roo.log('add add pane handler');
28111 this.on('addpane', this.onAddPane, this);
28114 * Updates the box title
28115 * @param {String} html to set the title to.
28117 setTitle : function(value)
28119 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28121 onAddPane : function(pane)
28123 this.panes.push(pane);
28124 //Roo.log('addpane');
28126 // tabs are rendere left to right..
28127 if(!this.showtabs){
28131 var ctr = this.el.select('.nav-tabs', true).first();
28134 var existing = ctr.select('.nav-tab',true);
28135 var qty = existing.getCount();;
28138 var tab = ctr.createChild({
28140 cls : 'nav-tab' + (qty ? '' : ' active'),
28148 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28151 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28153 pane.el.addClass('active');
28158 onTabClick : function(ev,un,ob,pane)
28160 //Roo.log('tab - prev default');
28161 ev.preventDefault();
28164 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28165 pane.tab.addClass('active');
28166 //Roo.log(pane.title);
28167 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28168 // technically we should have a deactivate event.. but maybe add later.
28169 // and it should not de-activate the selected tab...
28170 this.fireEvent('activatepane', pane);
28171 pane.el.addClass('active');
28172 pane.fireEvent('activate');
28177 getActivePane : function()
28180 Roo.each(this.panes, function(p) {
28181 if(p.el.hasClass('active')){
28202 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28204 * @class Roo.bootstrap.TabPane
28205 * @extends Roo.bootstrap.Component
28206 * Bootstrap TabPane class
28207 * @cfg {Boolean} active (false | true) Default false
28208 * @cfg {String} title title of panel
28212 * Create a new TabPane
28213 * @param {Object} config The config object
28216 Roo.bootstrap.dash.TabPane = function(config){
28217 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28223 * When a pane is activated
28224 * @param {Roo.bootstrap.dash.TabPane} pane
28231 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28236 // the tabBox that this is attached to.
28239 getAutoCreate : function()
28247 cfg.cls += ' active';
28252 initEvents : function()
28254 //Roo.log('trigger add pane handler');
28255 this.parent().fireEvent('addpane', this)
28259 * Updates the tab title
28260 * @param {String} html to set the title to.
28262 setTitle: function(str)
28268 this.tab.select('a', true).first().dom.innerHTML = str;
28285 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28288 * @class Roo.bootstrap.menu.Menu
28289 * @extends Roo.bootstrap.Component
28290 * Bootstrap Menu class - container for Menu
28291 * @cfg {String} html Text of the menu
28292 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28293 * @cfg {String} icon Font awesome icon
28294 * @cfg {String} pos Menu align to (top | bottom) default bottom
28298 * Create a new Menu
28299 * @param {Object} config The config object
28303 Roo.bootstrap.menu.Menu = function(config){
28304 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28308 * @event beforeshow
28309 * Fires before this menu is displayed
28310 * @param {Roo.bootstrap.menu.Menu} this
28314 * @event beforehide
28315 * Fires before this menu is hidden
28316 * @param {Roo.bootstrap.menu.Menu} this
28321 * Fires after this menu is displayed
28322 * @param {Roo.bootstrap.menu.Menu} this
28327 * Fires after this menu is hidden
28328 * @param {Roo.bootstrap.menu.Menu} this
28333 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28334 * @param {Roo.bootstrap.menu.Menu} this
28335 * @param {Roo.EventObject} e
28342 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28346 weight : 'default',
28351 getChildContainer : function() {
28352 if(this.isSubMenu){
28356 return this.el.select('ul.dropdown-menu', true).first();
28359 getAutoCreate : function()
28364 cls : 'roo-menu-text',
28372 cls : 'fa ' + this.icon
28383 cls : 'dropdown-button btn btn-' + this.weight,
28388 cls : 'dropdown-toggle btn btn-' + this.weight,
28398 cls : 'dropdown-menu'
28404 if(this.pos == 'top'){
28405 cfg.cls += ' dropup';
28408 if(this.isSubMenu){
28411 cls : 'dropdown-menu'
28418 onRender : function(ct, position)
28420 this.isSubMenu = ct.hasClass('dropdown-submenu');
28422 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28425 initEvents : function()
28427 if(this.isSubMenu){
28431 this.hidden = true;
28433 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28434 this.triggerEl.on('click', this.onTriggerPress, this);
28436 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28437 this.buttonEl.on('click', this.onClick, this);
28443 if(this.isSubMenu){
28447 return this.el.select('ul.dropdown-menu', true).first();
28450 onClick : function(e)
28452 this.fireEvent("click", this, e);
28455 onTriggerPress : function(e)
28457 if (this.isVisible()) {
28464 isVisible : function(){
28465 return !this.hidden;
28470 this.fireEvent("beforeshow", this);
28472 this.hidden = false;
28473 this.el.addClass('open');
28475 Roo.get(document).on("mouseup", this.onMouseUp, this);
28477 this.fireEvent("show", this);
28484 this.fireEvent("beforehide", this);
28486 this.hidden = true;
28487 this.el.removeClass('open');
28489 Roo.get(document).un("mouseup", this.onMouseUp);
28491 this.fireEvent("hide", this);
28494 onMouseUp : function()
28508 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28511 * @class Roo.bootstrap.menu.Item
28512 * @extends Roo.bootstrap.Component
28513 * Bootstrap MenuItem class
28514 * @cfg {Boolean} submenu (true | false) default false
28515 * @cfg {String} html text of the item
28516 * @cfg {String} href the link
28517 * @cfg {Boolean} disable (true | false) default false
28518 * @cfg {Boolean} preventDefault (true | false) default true
28519 * @cfg {String} icon Font awesome icon
28520 * @cfg {String} pos Submenu align to (left | right) default right
28524 * Create a new Item
28525 * @param {Object} config The config object
28529 Roo.bootstrap.menu.Item = function(config){
28530 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28534 * Fires when the mouse is hovering over this menu
28535 * @param {Roo.bootstrap.menu.Item} this
28536 * @param {Roo.EventObject} e
28541 * Fires when the mouse exits this menu
28542 * @param {Roo.bootstrap.menu.Item} this
28543 * @param {Roo.EventObject} e
28549 * The raw click event for the entire grid.
28550 * @param {Roo.EventObject} e
28556 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28561 preventDefault: true,
28566 getAutoCreate : function()
28571 cls : 'roo-menu-item-text',
28579 cls : 'fa ' + this.icon
28588 href : this.href || '#',
28595 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28599 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28601 if(this.pos == 'left'){
28602 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28609 initEvents : function()
28611 this.el.on('mouseover', this.onMouseOver, this);
28612 this.el.on('mouseout', this.onMouseOut, this);
28614 this.el.select('a', true).first().on('click', this.onClick, this);
28618 onClick : function(e)
28620 if(this.preventDefault){
28621 e.preventDefault();
28624 this.fireEvent("click", this, e);
28627 onMouseOver : function(e)
28629 if(this.submenu && this.pos == 'left'){
28630 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28633 this.fireEvent("mouseover", this, e);
28636 onMouseOut : function(e)
28638 this.fireEvent("mouseout", this, e);
28650 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28653 * @class Roo.bootstrap.menu.Separator
28654 * @extends Roo.bootstrap.Component
28655 * Bootstrap Separator class
28658 * Create a new Separator
28659 * @param {Object} config The config object
28663 Roo.bootstrap.menu.Separator = function(config){
28664 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28667 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28669 getAutoCreate : function(){
28690 * @class Roo.bootstrap.Tooltip
28691 * Bootstrap Tooltip class
28692 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28693 * to determine which dom element triggers the tooltip.
28695 * It needs to add support for additional attributes like tooltip-position
28698 * Create a new Toolti
28699 * @param {Object} config The config object
28702 Roo.bootstrap.Tooltip = function(config){
28703 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28705 this.alignment = Roo.bootstrap.Tooltip.alignment;
28707 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28708 this.alignment = config.alignment;
28713 Roo.apply(Roo.bootstrap.Tooltip, {
28715 * @function init initialize tooltip monitoring.
28719 currentTip : false,
28720 currentRegion : false,
28726 Roo.get(document).on('mouseover', this.enter ,this);
28727 Roo.get(document).on('mouseout', this.leave, this);
28730 this.currentTip = new Roo.bootstrap.Tooltip();
28733 enter : function(ev)
28735 var dom = ev.getTarget();
28737 //Roo.log(['enter',dom]);
28738 var el = Roo.fly(dom);
28739 if (this.currentEl) {
28741 //Roo.log(this.currentEl);
28742 //Roo.log(this.currentEl.contains(dom));
28743 if (this.currentEl == el) {
28746 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28752 if (this.currentTip.el) {
28753 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28757 if(!el || el.dom == document){
28763 // you can not look for children, as if el is the body.. then everythign is the child..
28764 if (!el.attr('tooltip')) { //
28765 if (!el.select("[tooltip]").elements.length) {
28768 // is the mouse over this child...?
28769 bindEl = el.select("[tooltip]").first();
28770 var xy = ev.getXY();
28771 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28772 //Roo.log("not in region.");
28775 //Roo.log("child element over..");
28778 this.currentEl = bindEl;
28779 this.currentTip.bind(bindEl);
28780 this.currentRegion = Roo.lib.Region.getRegion(dom);
28781 this.currentTip.enter();
28784 leave : function(ev)
28786 var dom = ev.getTarget();
28787 //Roo.log(['leave',dom]);
28788 if (!this.currentEl) {
28793 if (dom != this.currentEl.dom) {
28796 var xy = ev.getXY();
28797 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28800 // only activate leave if mouse cursor is outside... bounding box..
28805 if (this.currentTip) {
28806 this.currentTip.leave();
28808 //Roo.log('clear currentEl');
28809 this.currentEl = false;
28814 'left' : ['r-l', [-2,0], 'right'],
28815 'right' : ['l-r', [2,0], 'left'],
28816 'bottom' : ['t-b', [0,2], 'top'],
28817 'top' : [ 'b-t', [0,-2], 'bottom']
28823 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28828 delay : null, // can be { show : 300 , hide: 500}
28832 hoverState : null, //???
28834 placement : 'bottom',
28838 getAutoCreate : function(){
28845 cls : 'tooltip-arrow arrow'
28848 cls : 'tooltip-inner'
28855 bind : function(el)
28860 initEvents : function()
28862 this.arrowEl = this.el.select('.arrow', true).first();
28863 this.innerEl = this.el.select('.tooltip-inner', true).first();
28866 enter : function () {
28868 if (this.timeout != null) {
28869 clearTimeout(this.timeout);
28872 this.hoverState = 'in';
28873 //Roo.log("enter - show");
28874 if (!this.delay || !this.delay.show) {
28879 this.timeout = setTimeout(function () {
28880 if (_t.hoverState == 'in') {
28883 }, this.delay.show);
28887 clearTimeout(this.timeout);
28889 this.hoverState = 'out';
28890 if (!this.delay || !this.delay.hide) {
28896 this.timeout = setTimeout(function () {
28897 //Roo.log("leave - timeout");
28899 if (_t.hoverState == 'out') {
28901 Roo.bootstrap.Tooltip.currentEl = false;
28906 show : function (msg)
28909 this.render(document.body);
28912 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28914 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28916 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28918 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28919 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28921 var placement = typeof this.placement == 'function' ?
28922 this.placement.call(this, this.el, on_el) :
28925 var autoToken = /\s?auto?\s?/i;
28926 var autoPlace = autoToken.test(placement);
28928 placement = placement.replace(autoToken, '') || 'top';
28932 //this.el.setXY([0,0]);
28934 //this.el.dom.style.display='block';
28936 //this.el.appendTo(on_el);
28938 var p = this.getPosition();
28939 var box = this.el.getBox();
28945 var align = this.alignment[placement];
28947 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28949 if(placement == 'top' || placement == 'bottom'){
28951 placement = 'right';
28954 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28955 placement = 'left';
28958 var scroll = Roo.select('body', true).first().getScroll();
28960 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28964 align = this.alignment[placement];
28966 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28970 this.el.alignTo(this.bindEl, align[0],align[1]);
28971 //var arrow = this.el.select('.arrow',true).first();
28972 //arrow.set(align[2],
28974 this.el.addClass(placement);
28975 this.el.addClass("bs-tooltip-"+ placement);
28977 this.el.addClass('in fade show');
28979 this.hoverState = null;
28981 if (this.el.hasClass('fade')) {
28996 //this.el.setXY([0,0]);
28997 this.el.removeClass(['show', 'in']);
29013 * @class Roo.bootstrap.LocationPicker
29014 * @extends Roo.bootstrap.Component
29015 * Bootstrap LocationPicker class
29016 * @cfg {Number} latitude Position when init default 0
29017 * @cfg {Number} longitude Position when init default 0
29018 * @cfg {Number} zoom default 15
29019 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29020 * @cfg {Boolean} mapTypeControl default false
29021 * @cfg {Boolean} disableDoubleClickZoom default false
29022 * @cfg {Boolean} scrollwheel default true
29023 * @cfg {Boolean} streetViewControl default false
29024 * @cfg {Number} radius default 0
29025 * @cfg {String} locationName
29026 * @cfg {Boolean} draggable default true
29027 * @cfg {Boolean} enableAutocomplete default false
29028 * @cfg {Boolean} enableReverseGeocode default true
29029 * @cfg {String} markerTitle
29032 * Create a new LocationPicker
29033 * @param {Object} config The config object
29037 Roo.bootstrap.LocationPicker = function(config){
29039 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29044 * Fires when the picker initialized.
29045 * @param {Roo.bootstrap.LocationPicker} this
29046 * @param {Google Location} location
29050 * @event positionchanged
29051 * Fires when the picker position changed.
29052 * @param {Roo.bootstrap.LocationPicker} this
29053 * @param {Google Location} location
29055 positionchanged : true,
29058 * Fires when the map resize.
29059 * @param {Roo.bootstrap.LocationPicker} this
29064 * Fires when the map show.
29065 * @param {Roo.bootstrap.LocationPicker} this
29070 * Fires when the map hide.
29071 * @param {Roo.bootstrap.LocationPicker} this
29076 * Fires when click the map.
29077 * @param {Roo.bootstrap.LocationPicker} this
29078 * @param {Map event} e
29082 * @event mapRightClick
29083 * Fires when right click the map.
29084 * @param {Roo.bootstrap.LocationPicker} this
29085 * @param {Map event} e
29087 mapRightClick : true,
29089 * @event markerClick
29090 * Fires when click the marker.
29091 * @param {Roo.bootstrap.LocationPicker} this
29092 * @param {Map event} e
29094 markerClick : true,
29096 * @event markerRightClick
29097 * Fires when right click the marker.
29098 * @param {Roo.bootstrap.LocationPicker} this
29099 * @param {Map event} e
29101 markerRightClick : true,
29103 * @event OverlayViewDraw
29104 * Fires when OverlayView Draw
29105 * @param {Roo.bootstrap.LocationPicker} this
29107 OverlayViewDraw : true,
29109 * @event OverlayViewOnAdd
29110 * Fires when OverlayView Draw
29111 * @param {Roo.bootstrap.LocationPicker} this
29113 OverlayViewOnAdd : true,
29115 * @event OverlayViewOnRemove
29116 * Fires when OverlayView Draw
29117 * @param {Roo.bootstrap.LocationPicker} this
29119 OverlayViewOnRemove : true,
29121 * @event OverlayViewShow
29122 * Fires when OverlayView Draw
29123 * @param {Roo.bootstrap.LocationPicker} this
29124 * @param {Pixel} cpx
29126 OverlayViewShow : true,
29128 * @event OverlayViewHide
29129 * Fires when OverlayView Draw
29130 * @param {Roo.bootstrap.LocationPicker} this
29132 OverlayViewHide : true,
29134 * @event loadexception
29135 * Fires when load google lib failed.
29136 * @param {Roo.bootstrap.LocationPicker} this
29138 loadexception : true
29143 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29145 gMapContext: false,
29151 mapTypeControl: false,
29152 disableDoubleClickZoom: false,
29154 streetViewControl: false,
29158 enableAutocomplete: false,
29159 enableReverseGeocode: true,
29162 getAutoCreate: function()
29167 cls: 'roo-location-picker'
29173 initEvents: function(ct, position)
29175 if(!this.el.getWidth() || this.isApplied()){
29179 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29184 initial: function()
29186 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29187 this.fireEvent('loadexception', this);
29191 if(!this.mapTypeId){
29192 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29195 this.gMapContext = this.GMapContext();
29197 this.initOverlayView();
29199 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29203 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29204 _this.setPosition(_this.gMapContext.marker.position);
29207 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29208 _this.fireEvent('mapClick', this, event);
29212 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29213 _this.fireEvent('mapRightClick', this, event);
29217 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29218 _this.fireEvent('markerClick', this, event);
29222 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29223 _this.fireEvent('markerRightClick', this, event);
29227 this.setPosition(this.gMapContext.location);
29229 this.fireEvent('initial', this, this.gMapContext.location);
29232 initOverlayView: function()
29236 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29240 _this.fireEvent('OverlayViewDraw', _this);
29245 _this.fireEvent('OverlayViewOnAdd', _this);
29248 onRemove: function()
29250 _this.fireEvent('OverlayViewOnRemove', _this);
29253 show: function(cpx)
29255 _this.fireEvent('OverlayViewShow', _this, cpx);
29260 _this.fireEvent('OverlayViewHide', _this);
29266 fromLatLngToContainerPixel: function(event)
29268 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29271 isApplied: function()
29273 return this.getGmapContext() == false ? false : true;
29276 getGmapContext: function()
29278 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29281 GMapContext: function()
29283 var position = new google.maps.LatLng(this.latitude, this.longitude);
29285 var _map = new google.maps.Map(this.el.dom, {
29288 mapTypeId: this.mapTypeId,
29289 mapTypeControl: this.mapTypeControl,
29290 disableDoubleClickZoom: this.disableDoubleClickZoom,
29291 scrollwheel: this.scrollwheel,
29292 streetViewControl: this.streetViewControl,
29293 locationName: this.locationName,
29294 draggable: this.draggable,
29295 enableAutocomplete: this.enableAutocomplete,
29296 enableReverseGeocode: this.enableReverseGeocode
29299 var _marker = new google.maps.Marker({
29300 position: position,
29302 title: this.markerTitle,
29303 draggable: this.draggable
29310 location: position,
29311 radius: this.radius,
29312 locationName: this.locationName,
29313 addressComponents: {
29314 formatted_address: null,
29315 addressLine1: null,
29316 addressLine2: null,
29318 streetNumber: null,
29322 stateOrProvince: null
29325 domContainer: this.el.dom,
29326 geodecoder: new google.maps.Geocoder()
29330 drawCircle: function(center, radius, options)
29332 if (this.gMapContext.circle != null) {
29333 this.gMapContext.circle.setMap(null);
29337 options = Roo.apply({}, options, {
29338 strokeColor: "#0000FF",
29339 strokeOpacity: .35,
29341 fillColor: "#0000FF",
29345 options.map = this.gMapContext.map;
29346 options.radius = radius;
29347 options.center = center;
29348 this.gMapContext.circle = new google.maps.Circle(options);
29349 return this.gMapContext.circle;
29355 setPosition: function(location)
29357 this.gMapContext.location = location;
29358 this.gMapContext.marker.setPosition(location);
29359 this.gMapContext.map.panTo(location);
29360 this.drawCircle(location, this.gMapContext.radius, {});
29364 if (this.gMapContext.settings.enableReverseGeocode) {
29365 this.gMapContext.geodecoder.geocode({
29366 latLng: this.gMapContext.location
29367 }, function(results, status) {
29369 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29370 _this.gMapContext.locationName = results[0].formatted_address;
29371 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29373 _this.fireEvent('positionchanged', this, location);
29380 this.fireEvent('positionchanged', this, location);
29385 google.maps.event.trigger(this.gMapContext.map, "resize");
29387 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29389 this.fireEvent('resize', this);
29392 setPositionByLatLng: function(latitude, longitude)
29394 this.setPosition(new google.maps.LatLng(latitude, longitude));
29397 getCurrentPosition: function()
29400 latitude: this.gMapContext.location.lat(),
29401 longitude: this.gMapContext.location.lng()
29405 getAddressName: function()
29407 return this.gMapContext.locationName;
29410 getAddressComponents: function()
29412 return this.gMapContext.addressComponents;
29415 address_component_from_google_geocode: function(address_components)
29419 for (var i = 0; i < address_components.length; i++) {
29420 var component = address_components[i];
29421 if (component.types.indexOf("postal_code") >= 0) {
29422 result.postalCode = component.short_name;
29423 } else if (component.types.indexOf("street_number") >= 0) {
29424 result.streetNumber = component.short_name;
29425 } else if (component.types.indexOf("route") >= 0) {
29426 result.streetName = component.short_name;
29427 } else if (component.types.indexOf("neighborhood") >= 0) {
29428 result.city = component.short_name;
29429 } else if (component.types.indexOf("locality") >= 0) {
29430 result.city = component.short_name;
29431 } else if (component.types.indexOf("sublocality") >= 0) {
29432 result.district = component.short_name;
29433 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29434 result.stateOrProvince = component.short_name;
29435 } else if (component.types.indexOf("country") >= 0) {
29436 result.country = component.short_name;
29440 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29441 result.addressLine2 = "";
29445 setZoomLevel: function(zoom)
29447 this.gMapContext.map.setZoom(zoom);
29460 this.fireEvent('show', this);
29471 this.fireEvent('hide', this);
29476 Roo.apply(Roo.bootstrap.LocationPicker, {
29478 OverlayView : function(map, options)
29480 options = options || {};
29487 * @class Roo.bootstrap.Alert
29488 * @extends Roo.bootstrap.Component
29489 * Bootstrap Alert class - shows an alert area box
29491 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29492 Enter a valid email address
29495 * @cfg {String} title The title of alert
29496 * @cfg {String} html The content of alert
29497 * @cfg {String} weight ( success | info | warning | danger )
29498 * @cfg {String} faicon font-awesomeicon
29501 * Create a new alert
29502 * @param {Object} config The config object
29506 Roo.bootstrap.Alert = function(config){
29507 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29511 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29518 getAutoCreate : function()
29527 cls : 'roo-alert-icon'
29532 cls : 'roo-alert-title',
29537 cls : 'roo-alert-text',
29544 cfg.cn[0].cls += ' fa ' + this.faicon;
29548 cfg.cls += ' alert-' + this.weight;
29554 initEvents: function()
29556 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29559 setTitle : function(str)
29561 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29564 setText : function(str)
29566 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29569 setWeight : function(weight)
29572 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29575 this.weight = weight;
29577 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29580 setIcon : function(icon)
29583 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29586 this.faicon = icon;
29588 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29609 * @class Roo.bootstrap.UploadCropbox
29610 * @extends Roo.bootstrap.Component
29611 * Bootstrap UploadCropbox class
29612 * @cfg {String} emptyText show when image has been loaded
29613 * @cfg {String} rotateNotify show when image too small to rotate
29614 * @cfg {Number} errorTimeout default 3000
29615 * @cfg {Number} minWidth default 300
29616 * @cfg {Number} minHeight default 300
29617 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29618 * @cfg {Boolean} isDocument (true|false) default false
29619 * @cfg {String} url action url
29620 * @cfg {String} paramName default 'imageUpload'
29621 * @cfg {String} method default POST
29622 * @cfg {Boolean} loadMask (true|false) default true
29623 * @cfg {Boolean} loadingText default 'Loading...'
29626 * Create a new UploadCropbox
29627 * @param {Object} config The config object
29630 Roo.bootstrap.UploadCropbox = function(config){
29631 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29635 * @event beforeselectfile
29636 * Fire before select file
29637 * @param {Roo.bootstrap.UploadCropbox} this
29639 "beforeselectfile" : true,
29642 * Fire after initEvent
29643 * @param {Roo.bootstrap.UploadCropbox} this
29648 * Fire after initEvent
29649 * @param {Roo.bootstrap.UploadCropbox} this
29650 * @param {String} data
29655 * Fire when preparing the file data
29656 * @param {Roo.bootstrap.UploadCropbox} this
29657 * @param {Object} file
29662 * Fire when get exception
29663 * @param {Roo.bootstrap.UploadCropbox} this
29664 * @param {XMLHttpRequest} xhr
29666 "exception" : true,
29668 * @event beforeloadcanvas
29669 * Fire before load the canvas
29670 * @param {Roo.bootstrap.UploadCropbox} this
29671 * @param {String} src
29673 "beforeloadcanvas" : true,
29676 * Fire when trash image
29677 * @param {Roo.bootstrap.UploadCropbox} this
29682 * Fire when download the image
29683 * @param {Roo.bootstrap.UploadCropbox} this
29687 * @event footerbuttonclick
29688 * Fire when footerbuttonclick
29689 * @param {Roo.bootstrap.UploadCropbox} this
29690 * @param {String} type
29692 "footerbuttonclick" : true,
29696 * @param {Roo.bootstrap.UploadCropbox} this
29701 * Fire when rotate the image
29702 * @param {Roo.bootstrap.UploadCropbox} this
29703 * @param {String} pos
29708 * Fire when inspect the file
29709 * @param {Roo.bootstrap.UploadCropbox} this
29710 * @param {Object} file
29715 * Fire when xhr upload the file
29716 * @param {Roo.bootstrap.UploadCropbox} this
29717 * @param {Object} data
29722 * Fire when arrange the file data
29723 * @param {Roo.bootstrap.UploadCropbox} this
29724 * @param {Object} formData
29729 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29732 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29734 emptyText : 'Click to upload image',
29735 rotateNotify : 'Image is too small to rotate',
29736 errorTimeout : 3000,
29750 cropType : 'image/jpeg',
29752 canvasLoaded : false,
29753 isDocument : false,
29755 paramName : 'imageUpload',
29757 loadingText : 'Loading...',
29760 getAutoCreate : function()
29764 cls : 'roo-upload-cropbox',
29768 cls : 'roo-upload-cropbox-selector',
29773 cls : 'roo-upload-cropbox-body',
29774 style : 'cursor:pointer',
29778 cls : 'roo-upload-cropbox-preview'
29782 cls : 'roo-upload-cropbox-thumb'
29786 cls : 'roo-upload-cropbox-empty-notify',
29787 html : this.emptyText
29791 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29792 html : this.rotateNotify
29798 cls : 'roo-upload-cropbox-footer',
29801 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29811 onRender : function(ct, position)
29813 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29815 if (this.buttons.length) {
29817 Roo.each(this.buttons, function(bb) {
29819 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29821 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29827 this.maskEl = this.el;
29831 initEvents : function()
29833 this.urlAPI = (window.createObjectURL && window) ||
29834 (window.URL && URL.revokeObjectURL && URL) ||
29835 (window.webkitURL && webkitURL);
29837 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29838 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29840 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29841 this.selectorEl.hide();
29843 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29844 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29846 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29847 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29848 this.thumbEl.hide();
29850 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29851 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29853 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29854 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29855 this.errorEl.hide();
29857 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29858 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29859 this.footerEl.hide();
29861 this.setThumbBoxSize();
29867 this.fireEvent('initial', this);
29874 window.addEventListener("resize", function() { _this.resize(); } );
29876 this.bodyEl.on('click', this.beforeSelectFile, this);
29879 this.bodyEl.on('touchstart', this.onTouchStart, this);
29880 this.bodyEl.on('touchmove', this.onTouchMove, this);
29881 this.bodyEl.on('touchend', this.onTouchEnd, this);
29885 this.bodyEl.on('mousedown', this.onMouseDown, this);
29886 this.bodyEl.on('mousemove', this.onMouseMove, this);
29887 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29888 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29889 Roo.get(document).on('mouseup', this.onMouseUp, this);
29892 this.selectorEl.on('change', this.onFileSelected, this);
29898 this.baseScale = 1;
29900 this.baseRotate = 1;
29901 this.dragable = false;
29902 this.pinching = false;
29905 this.cropData = false;
29906 this.notifyEl.dom.innerHTML = this.emptyText;
29908 this.selectorEl.dom.value = '';
29912 resize : function()
29914 if(this.fireEvent('resize', this) != false){
29915 this.setThumbBoxPosition();
29916 this.setCanvasPosition();
29920 onFooterButtonClick : function(e, el, o, type)
29923 case 'rotate-left' :
29924 this.onRotateLeft(e);
29926 case 'rotate-right' :
29927 this.onRotateRight(e);
29930 this.beforeSelectFile(e);
29945 this.fireEvent('footerbuttonclick', this, type);
29948 beforeSelectFile : function(e)
29950 e.preventDefault();
29952 if(this.fireEvent('beforeselectfile', this) != false){
29953 this.selectorEl.dom.click();
29957 onFileSelected : function(e)
29959 e.preventDefault();
29961 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29965 var file = this.selectorEl.dom.files[0];
29967 if(this.fireEvent('inspect', this, file) != false){
29968 this.prepare(file);
29973 trash : function(e)
29975 this.fireEvent('trash', this);
29978 download : function(e)
29980 this.fireEvent('download', this);
29983 loadCanvas : function(src)
29985 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29989 this.imageEl = document.createElement('img');
29993 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29995 this.imageEl.src = src;
29999 onLoadCanvas : function()
30001 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30002 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30004 this.bodyEl.un('click', this.beforeSelectFile, this);
30006 this.notifyEl.hide();
30007 this.thumbEl.show();
30008 this.footerEl.show();
30010 this.baseRotateLevel();
30012 if(this.isDocument){
30013 this.setThumbBoxSize();
30016 this.setThumbBoxPosition();
30018 this.baseScaleLevel();
30024 this.canvasLoaded = true;
30027 this.maskEl.unmask();
30032 setCanvasPosition : function()
30034 if(!this.canvasEl){
30038 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30039 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30041 this.previewEl.setLeft(pw);
30042 this.previewEl.setTop(ph);
30046 onMouseDown : function(e)
30050 this.dragable = true;
30051 this.pinching = false;
30053 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30054 this.dragable = false;
30058 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30059 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30063 onMouseMove : function(e)
30067 if(!this.canvasLoaded){
30071 if (!this.dragable){
30075 var minX = Math.ceil(this.thumbEl.getLeft(true));
30076 var minY = Math.ceil(this.thumbEl.getTop(true));
30078 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30079 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30081 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30082 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30084 x = x - this.mouseX;
30085 y = y - this.mouseY;
30087 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30088 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30090 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30091 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30093 this.previewEl.setLeft(bgX);
30094 this.previewEl.setTop(bgY);
30096 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30097 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30100 onMouseUp : function(e)
30104 this.dragable = false;
30107 onMouseWheel : function(e)
30111 this.startScale = this.scale;
30113 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30115 if(!this.zoomable()){
30116 this.scale = this.startScale;
30125 zoomable : function()
30127 var minScale = this.thumbEl.getWidth() / this.minWidth;
30129 if(this.minWidth < this.minHeight){
30130 minScale = this.thumbEl.getHeight() / this.minHeight;
30133 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30134 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30138 (this.rotate == 0 || this.rotate == 180) &&
30140 width > this.imageEl.OriginWidth ||
30141 height > this.imageEl.OriginHeight ||
30142 (width < this.minWidth && height < this.minHeight)
30150 (this.rotate == 90 || this.rotate == 270) &&
30152 width > this.imageEl.OriginWidth ||
30153 height > this.imageEl.OriginHeight ||
30154 (width < this.minHeight && height < this.minWidth)
30161 !this.isDocument &&
30162 (this.rotate == 0 || this.rotate == 180) &&
30164 width < this.minWidth ||
30165 width > this.imageEl.OriginWidth ||
30166 height < this.minHeight ||
30167 height > this.imageEl.OriginHeight
30174 !this.isDocument &&
30175 (this.rotate == 90 || this.rotate == 270) &&
30177 width < this.minHeight ||
30178 width > this.imageEl.OriginWidth ||
30179 height < this.minWidth ||
30180 height > this.imageEl.OriginHeight
30190 onRotateLeft : function(e)
30192 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30194 var minScale = this.thumbEl.getWidth() / this.minWidth;
30196 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30197 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30199 this.startScale = this.scale;
30201 while (this.getScaleLevel() < minScale){
30203 this.scale = this.scale + 1;
30205 if(!this.zoomable()){
30210 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30211 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30216 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30223 this.scale = this.startScale;
30225 this.onRotateFail();
30230 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30232 if(this.isDocument){
30233 this.setThumbBoxSize();
30234 this.setThumbBoxPosition();
30235 this.setCanvasPosition();
30240 this.fireEvent('rotate', this, 'left');
30244 onRotateRight : function(e)
30246 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30248 var minScale = this.thumbEl.getWidth() / this.minWidth;
30250 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30251 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30253 this.startScale = this.scale;
30255 while (this.getScaleLevel() < minScale){
30257 this.scale = this.scale + 1;
30259 if(!this.zoomable()){
30264 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30265 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30270 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30277 this.scale = this.startScale;
30279 this.onRotateFail();
30284 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30286 if(this.isDocument){
30287 this.setThumbBoxSize();
30288 this.setThumbBoxPosition();
30289 this.setCanvasPosition();
30294 this.fireEvent('rotate', this, 'right');
30297 onRotateFail : function()
30299 this.errorEl.show(true);
30303 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30308 this.previewEl.dom.innerHTML = '';
30310 var canvasEl = document.createElement("canvas");
30312 var contextEl = canvasEl.getContext("2d");
30314 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30315 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30316 var center = this.imageEl.OriginWidth / 2;
30318 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30319 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30320 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30321 center = this.imageEl.OriginHeight / 2;
30324 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30326 contextEl.translate(center, center);
30327 contextEl.rotate(this.rotate * Math.PI / 180);
30329 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30331 this.canvasEl = document.createElement("canvas");
30333 this.contextEl = this.canvasEl.getContext("2d");
30335 switch (this.rotate) {
30338 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30339 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30341 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30346 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30347 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30349 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30350 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);
30354 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30359 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30360 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30362 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30363 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);
30367 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);
30372 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30373 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30375 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30376 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30380 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);
30387 this.previewEl.appendChild(this.canvasEl);
30389 this.setCanvasPosition();
30394 if(!this.canvasLoaded){
30398 var imageCanvas = document.createElement("canvas");
30400 var imageContext = imageCanvas.getContext("2d");
30402 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30403 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30405 var center = imageCanvas.width / 2;
30407 imageContext.translate(center, center);
30409 imageContext.rotate(this.rotate * Math.PI / 180);
30411 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30413 var canvas = document.createElement("canvas");
30415 var context = canvas.getContext("2d");
30417 canvas.width = this.minWidth;
30418 canvas.height = this.minHeight;
30420 switch (this.rotate) {
30423 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30424 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30426 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30427 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30429 var targetWidth = this.minWidth - 2 * x;
30430 var targetHeight = this.minHeight - 2 * y;
30434 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30435 scale = targetWidth / width;
30438 if(x > 0 && y == 0){
30439 scale = targetHeight / height;
30442 if(x > 0 && y > 0){
30443 scale = targetWidth / width;
30445 if(width < height){
30446 scale = targetHeight / height;
30450 context.scale(scale, scale);
30452 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30453 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30455 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30456 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30458 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30463 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30464 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30466 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30467 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30469 var targetWidth = this.minWidth - 2 * x;
30470 var targetHeight = this.minHeight - 2 * y;
30474 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30475 scale = targetWidth / width;
30478 if(x > 0 && y == 0){
30479 scale = targetHeight / height;
30482 if(x > 0 && y > 0){
30483 scale = targetWidth / width;
30485 if(width < height){
30486 scale = targetHeight / height;
30490 context.scale(scale, scale);
30492 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30493 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30495 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30496 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30498 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30500 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30505 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30506 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30508 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30509 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30511 var targetWidth = this.minWidth - 2 * x;
30512 var targetHeight = this.minHeight - 2 * y;
30516 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30517 scale = targetWidth / width;
30520 if(x > 0 && y == 0){
30521 scale = targetHeight / height;
30524 if(x > 0 && y > 0){
30525 scale = targetWidth / width;
30527 if(width < height){
30528 scale = targetHeight / height;
30532 context.scale(scale, scale);
30534 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30535 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30537 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30538 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30540 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30541 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30543 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30548 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30549 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30551 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30552 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30554 var targetWidth = this.minWidth - 2 * x;
30555 var targetHeight = this.minHeight - 2 * y;
30559 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30560 scale = targetWidth / width;
30563 if(x > 0 && y == 0){
30564 scale = targetHeight / height;
30567 if(x > 0 && y > 0){
30568 scale = targetWidth / width;
30570 if(width < height){
30571 scale = targetHeight / height;
30575 context.scale(scale, scale);
30577 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30578 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30580 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30581 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30583 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30585 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30592 this.cropData = canvas.toDataURL(this.cropType);
30594 if(this.fireEvent('crop', this, this.cropData) !== false){
30595 this.process(this.file, this.cropData);
30602 setThumbBoxSize : function()
30606 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30607 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30608 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30610 this.minWidth = width;
30611 this.minHeight = height;
30613 if(this.rotate == 90 || this.rotate == 270){
30614 this.minWidth = height;
30615 this.minHeight = width;
30620 width = Math.ceil(this.minWidth * height / this.minHeight);
30622 if(this.minWidth > this.minHeight){
30624 height = Math.ceil(this.minHeight * width / this.minWidth);
30627 this.thumbEl.setStyle({
30628 width : width + 'px',
30629 height : height + 'px'
30636 setThumbBoxPosition : function()
30638 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30639 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30641 this.thumbEl.setLeft(x);
30642 this.thumbEl.setTop(y);
30646 baseRotateLevel : function()
30648 this.baseRotate = 1;
30651 typeof(this.exif) != 'undefined' &&
30652 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30653 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30655 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30658 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30662 baseScaleLevel : function()
30666 if(this.isDocument){
30668 if(this.baseRotate == 6 || this.baseRotate == 8){
30670 height = this.thumbEl.getHeight();
30671 this.baseScale = height / this.imageEl.OriginWidth;
30673 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30674 width = this.thumbEl.getWidth();
30675 this.baseScale = width / this.imageEl.OriginHeight;
30681 height = this.thumbEl.getHeight();
30682 this.baseScale = height / this.imageEl.OriginHeight;
30684 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30685 width = this.thumbEl.getWidth();
30686 this.baseScale = width / this.imageEl.OriginWidth;
30692 if(this.baseRotate == 6 || this.baseRotate == 8){
30694 width = this.thumbEl.getHeight();
30695 this.baseScale = width / this.imageEl.OriginHeight;
30697 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30698 height = this.thumbEl.getWidth();
30699 this.baseScale = height / this.imageEl.OriginHeight;
30702 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30703 height = this.thumbEl.getWidth();
30704 this.baseScale = height / this.imageEl.OriginHeight;
30706 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30707 width = this.thumbEl.getHeight();
30708 this.baseScale = width / this.imageEl.OriginWidth;
30715 width = this.thumbEl.getWidth();
30716 this.baseScale = width / this.imageEl.OriginWidth;
30718 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30719 height = this.thumbEl.getHeight();
30720 this.baseScale = height / this.imageEl.OriginHeight;
30723 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30725 height = this.thumbEl.getHeight();
30726 this.baseScale = height / this.imageEl.OriginHeight;
30728 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30729 width = this.thumbEl.getWidth();
30730 this.baseScale = width / this.imageEl.OriginWidth;
30738 getScaleLevel : function()
30740 return this.baseScale * Math.pow(1.1, this.scale);
30743 onTouchStart : function(e)
30745 if(!this.canvasLoaded){
30746 this.beforeSelectFile(e);
30750 var touches = e.browserEvent.touches;
30756 if(touches.length == 1){
30757 this.onMouseDown(e);
30761 if(touches.length != 2){
30767 for(var i = 0, finger; finger = touches[i]; i++){
30768 coords.push(finger.pageX, finger.pageY);
30771 var x = Math.pow(coords[0] - coords[2], 2);
30772 var y = Math.pow(coords[1] - coords[3], 2);
30774 this.startDistance = Math.sqrt(x + y);
30776 this.startScale = this.scale;
30778 this.pinching = true;
30779 this.dragable = false;
30783 onTouchMove : function(e)
30785 if(!this.pinching && !this.dragable){
30789 var touches = e.browserEvent.touches;
30796 this.onMouseMove(e);
30802 for(var i = 0, finger; finger = touches[i]; i++){
30803 coords.push(finger.pageX, finger.pageY);
30806 var x = Math.pow(coords[0] - coords[2], 2);
30807 var y = Math.pow(coords[1] - coords[3], 2);
30809 this.endDistance = Math.sqrt(x + y);
30811 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30813 if(!this.zoomable()){
30814 this.scale = this.startScale;
30822 onTouchEnd : function(e)
30824 this.pinching = false;
30825 this.dragable = false;
30829 process : function(file, crop)
30832 this.maskEl.mask(this.loadingText);
30835 this.xhr = new XMLHttpRequest();
30837 file.xhr = this.xhr;
30839 this.xhr.open(this.method, this.url, true);
30842 "Accept": "application/json",
30843 "Cache-Control": "no-cache",
30844 "X-Requested-With": "XMLHttpRequest"
30847 for (var headerName in headers) {
30848 var headerValue = headers[headerName];
30850 this.xhr.setRequestHeader(headerName, headerValue);
30856 this.xhr.onload = function()
30858 _this.xhrOnLoad(_this.xhr);
30861 this.xhr.onerror = function()
30863 _this.xhrOnError(_this.xhr);
30866 var formData = new FormData();
30868 formData.append('returnHTML', 'NO');
30871 formData.append('crop', crop);
30874 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30875 formData.append(this.paramName, file, file.name);
30878 if(typeof(file.filename) != 'undefined'){
30879 formData.append('filename', file.filename);
30882 if(typeof(file.mimetype) != 'undefined'){
30883 formData.append('mimetype', file.mimetype);
30886 if(this.fireEvent('arrange', this, formData) != false){
30887 this.xhr.send(formData);
30891 xhrOnLoad : function(xhr)
30894 this.maskEl.unmask();
30897 if (xhr.readyState !== 4) {
30898 this.fireEvent('exception', this, xhr);
30902 var response = Roo.decode(xhr.responseText);
30904 if(!response.success){
30905 this.fireEvent('exception', this, xhr);
30909 var response = Roo.decode(xhr.responseText);
30911 this.fireEvent('upload', this, response);
30915 xhrOnError : function()
30918 this.maskEl.unmask();
30921 Roo.log('xhr on error');
30923 var response = Roo.decode(xhr.responseText);
30929 prepare : function(file)
30932 this.maskEl.mask(this.loadingText);
30938 if(typeof(file) === 'string'){
30939 this.loadCanvas(file);
30943 if(!file || !this.urlAPI){
30948 this.cropType = file.type;
30952 if(this.fireEvent('prepare', this, this.file) != false){
30954 var reader = new FileReader();
30956 reader.onload = function (e) {
30957 if (e.target.error) {
30958 Roo.log(e.target.error);
30962 var buffer = e.target.result,
30963 dataView = new DataView(buffer),
30965 maxOffset = dataView.byteLength - 4,
30969 if (dataView.getUint16(0) === 0xffd8) {
30970 while (offset < maxOffset) {
30971 markerBytes = dataView.getUint16(offset);
30973 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30974 markerLength = dataView.getUint16(offset + 2) + 2;
30975 if (offset + markerLength > dataView.byteLength) {
30976 Roo.log('Invalid meta data: Invalid segment size.');
30980 if(markerBytes == 0xffe1){
30981 _this.parseExifData(
30988 offset += markerLength;
30998 var url = _this.urlAPI.createObjectURL(_this.file);
31000 _this.loadCanvas(url);
31005 reader.readAsArrayBuffer(this.file);
31011 parseExifData : function(dataView, offset, length)
31013 var tiffOffset = offset + 10,
31017 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31018 // No Exif data, might be XMP data instead
31022 // Check for the ASCII code for "Exif" (0x45786966):
31023 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31024 // No Exif data, might be XMP data instead
31027 if (tiffOffset + 8 > dataView.byteLength) {
31028 Roo.log('Invalid Exif data: Invalid segment size.');
31031 // Check for the two null bytes:
31032 if (dataView.getUint16(offset + 8) !== 0x0000) {
31033 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31036 // Check the byte alignment:
31037 switch (dataView.getUint16(tiffOffset)) {
31039 littleEndian = true;
31042 littleEndian = false;
31045 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31048 // Check for the TIFF tag marker (0x002A):
31049 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31050 Roo.log('Invalid Exif data: Missing TIFF marker.');
31053 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31054 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31056 this.parseExifTags(
31059 tiffOffset + dirOffset,
31064 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31069 if (dirOffset + 6 > dataView.byteLength) {
31070 Roo.log('Invalid Exif data: Invalid directory offset.');
31073 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31074 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31075 if (dirEndOffset + 4 > dataView.byteLength) {
31076 Roo.log('Invalid Exif data: Invalid directory size.');
31079 for (i = 0; i < tagsNumber; i += 1) {
31083 dirOffset + 2 + 12 * i, // tag offset
31087 // Return the offset to the next directory:
31088 return dataView.getUint32(dirEndOffset, littleEndian);
31091 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31093 var tag = dataView.getUint16(offset, littleEndian);
31095 this.exif[tag] = this.getExifValue(
31099 dataView.getUint16(offset + 2, littleEndian), // tag type
31100 dataView.getUint32(offset + 4, littleEndian), // tag length
31105 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31107 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31116 Roo.log('Invalid Exif data: Invalid tag type.');
31120 tagSize = tagType.size * length;
31121 // Determine if the value is contained in the dataOffset bytes,
31122 // or if the value at the dataOffset is a pointer to the actual data:
31123 dataOffset = tagSize > 4 ?
31124 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31125 if (dataOffset + tagSize > dataView.byteLength) {
31126 Roo.log('Invalid Exif data: Invalid data offset.');
31129 if (length === 1) {
31130 return tagType.getValue(dataView, dataOffset, littleEndian);
31133 for (i = 0; i < length; i += 1) {
31134 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31137 if (tagType.ascii) {
31139 // Concatenate the chars:
31140 for (i = 0; i < values.length; i += 1) {
31142 // Ignore the terminating NULL byte(s):
31143 if (c === '\u0000') {
31155 Roo.apply(Roo.bootstrap.UploadCropbox, {
31157 'Orientation': 0x0112
31161 1: 0, //'top-left',
31163 3: 180, //'bottom-right',
31164 // 4: 'bottom-left',
31166 6: 90, //'right-top',
31167 // 7: 'right-bottom',
31168 8: 270 //'left-bottom'
31172 // byte, 8-bit unsigned int:
31174 getValue: function (dataView, dataOffset) {
31175 return dataView.getUint8(dataOffset);
31179 // ascii, 8-bit byte:
31181 getValue: function (dataView, dataOffset) {
31182 return String.fromCharCode(dataView.getUint8(dataOffset));
31187 // short, 16 bit int:
31189 getValue: function (dataView, dataOffset, littleEndian) {
31190 return dataView.getUint16(dataOffset, littleEndian);
31194 // long, 32 bit int:
31196 getValue: function (dataView, dataOffset, littleEndian) {
31197 return dataView.getUint32(dataOffset, littleEndian);
31201 // rational = two long values, first is numerator, second is denominator:
31203 getValue: function (dataView, dataOffset, littleEndian) {
31204 return dataView.getUint32(dataOffset, littleEndian) /
31205 dataView.getUint32(dataOffset + 4, littleEndian);
31209 // slong, 32 bit signed int:
31211 getValue: function (dataView, dataOffset, littleEndian) {
31212 return dataView.getInt32(dataOffset, littleEndian);
31216 // srational, two slongs, first is numerator, second is denominator:
31218 getValue: function (dataView, dataOffset, littleEndian) {
31219 return dataView.getInt32(dataOffset, littleEndian) /
31220 dataView.getInt32(dataOffset + 4, littleEndian);
31230 cls : 'btn-group roo-upload-cropbox-rotate-left',
31231 action : 'rotate-left',
31235 cls : 'btn btn-default',
31236 html : '<i class="fa fa-undo"></i>'
31242 cls : 'btn-group roo-upload-cropbox-picture',
31243 action : 'picture',
31247 cls : 'btn btn-default',
31248 html : '<i class="fa fa-picture-o"></i>'
31254 cls : 'btn-group roo-upload-cropbox-rotate-right',
31255 action : 'rotate-right',
31259 cls : 'btn btn-default',
31260 html : '<i class="fa fa-repeat"></i>'
31268 cls : 'btn-group roo-upload-cropbox-rotate-left',
31269 action : 'rotate-left',
31273 cls : 'btn btn-default',
31274 html : '<i class="fa fa-undo"></i>'
31280 cls : 'btn-group roo-upload-cropbox-download',
31281 action : 'download',
31285 cls : 'btn btn-default',
31286 html : '<i class="fa fa-download"></i>'
31292 cls : 'btn-group roo-upload-cropbox-crop',
31297 cls : 'btn btn-default',
31298 html : '<i class="fa fa-crop"></i>'
31304 cls : 'btn-group roo-upload-cropbox-trash',
31309 cls : 'btn btn-default',
31310 html : '<i class="fa fa-trash"></i>'
31316 cls : 'btn-group roo-upload-cropbox-rotate-right',
31317 action : 'rotate-right',
31321 cls : 'btn btn-default',
31322 html : '<i class="fa fa-repeat"></i>'
31330 cls : 'btn-group roo-upload-cropbox-rotate-left',
31331 action : 'rotate-left',
31335 cls : 'btn btn-default',
31336 html : '<i class="fa fa-undo"></i>'
31342 cls : 'btn-group roo-upload-cropbox-rotate-right',
31343 action : 'rotate-right',
31347 cls : 'btn btn-default',
31348 html : '<i class="fa fa-repeat"></i>'
31361 * @class Roo.bootstrap.DocumentManager
31362 * @extends Roo.bootstrap.Component
31363 * Bootstrap DocumentManager class
31364 * @cfg {String} paramName default 'imageUpload'
31365 * @cfg {String} toolTipName default 'filename'
31366 * @cfg {String} method default POST
31367 * @cfg {String} url action url
31368 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31369 * @cfg {Boolean} multiple multiple upload default true
31370 * @cfg {Number} thumbSize default 300
31371 * @cfg {String} fieldLabel
31372 * @cfg {Number} labelWidth default 4
31373 * @cfg {String} labelAlign (left|top) default left
31374 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31375 * @cfg {Number} labellg set the width of label (1-12)
31376 * @cfg {Number} labelmd set the width of label (1-12)
31377 * @cfg {Number} labelsm set the width of label (1-12)
31378 * @cfg {Number} labelxs set the width of label (1-12)
31381 * Create a new DocumentManager
31382 * @param {Object} config The config object
31385 Roo.bootstrap.DocumentManager = function(config){
31386 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31389 this.delegates = [];
31394 * Fire when initial the DocumentManager
31395 * @param {Roo.bootstrap.DocumentManager} this
31400 * inspect selected file
31401 * @param {Roo.bootstrap.DocumentManager} this
31402 * @param {File} file
31407 * Fire when xhr load exception
31408 * @param {Roo.bootstrap.DocumentManager} this
31409 * @param {XMLHttpRequest} xhr
31411 "exception" : true,
31413 * @event afterupload
31414 * Fire when xhr load exception
31415 * @param {Roo.bootstrap.DocumentManager} this
31416 * @param {XMLHttpRequest} xhr
31418 "afterupload" : true,
31421 * prepare the form data
31422 * @param {Roo.bootstrap.DocumentManager} this
31423 * @param {Object} formData
31428 * Fire when remove the file
31429 * @param {Roo.bootstrap.DocumentManager} this
31430 * @param {Object} file
31435 * Fire after refresh the file
31436 * @param {Roo.bootstrap.DocumentManager} this
31441 * Fire after click the image
31442 * @param {Roo.bootstrap.DocumentManager} this
31443 * @param {Object} file
31448 * Fire when upload a image and editable set to true
31449 * @param {Roo.bootstrap.DocumentManager} this
31450 * @param {Object} file
31454 * @event beforeselectfile
31455 * Fire before select file
31456 * @param {Roo.bootstrap.DocumentManager} this
31458 "beforeselectfile" : true,
31461 * Fire before process file
31462 * @param {Roo.bootstrap.DocumentManager} this
31463 * @param {Object} file
31467 * @event previewrendered
31468 * Fire when preview rendered
31469 * @param {Roo.bootstrap.DocumentManager} this
31470 * @param {Object} file
31472 "previewrendered" : true,
31475 "previewResize" : true
31480 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31489 paramName : 'imageUpload',
31490 toolTipName : 'filename',
31493 labelAlign : 'left',
31503 getAutoCreate : function()
31505 var managerWidget = {
31507 cls : 'roo-document-manager',
31511 cls : 'roo-document-manager-selector',
31516 cls : 'roo-document-manager-uploader',
31520 cls : 'roo-document-manager-upload-btn',
31521 html : '<i class="fa fa-plus"></i>'
31532 cls : 'column col-md-12',
31537 if(this.fieldLabel.length){
31542 cls : 'column col-md-12',
31543 html : this.fieldLabel
31547 cls : 'column col-md-12',
31552 if(this.labelAlign == 'left'){
31557 html : this.fieldLabel
31566 if(this.labelWidth > 12){
31567 content[0].style = "width: " + this.labelWidth + 'px';
31570 if(this.labelWidth < 13 && this.labelmd == 0){
31571 this.labelmd = this.labelWidth;
31574 if(this.labellg > 0){
31575 content[0].cls += ' col-lg-' + this.labellg;
31576 content[1].cls += ' col-lg-' + (12 - this.labellg);
31579 if(this.labelmd > 0){
31580 content[0].cls += ' col-md-' + this.labelmd;
31581 content[1].cls += ' col-md-' + (12 - this.labelmd);
31584 if(this.labelsm > 0){
31585 content[0].cls += ' col-sm-' + this.labelsm;
31586 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31589 if(this.labelxs > 0){
31590 content[0].cls += ' col-xs-' + this.labelxs;
31591 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31599 cls : 'row clearfix',
31607 initEvents : function()
31609 this.managerEl = this.el.select('.roo-document-manager', true).first();
31610 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31612 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31613 this.selectorEl.hide();
31616 this.selectorEl.attr('multiple', 'multiple');
31619 this.selectorEl.on('change', this.onFileSelected, this);
31621 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31622 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31624 this.uploader.on('click', this.onUploaderClick, this);
31626 this.renderProgressDialog();
31630 window.addEventListener("resize", function() { _this.refresh(); } );
31632 this.fireEvent('initial', this);
31635 renderProgressDialog : function()
31639 this.progressDialog = new Roo.bootstrap.Modal({
31640 cls : 'roo-document-manager-progress-dialog',
31641 allow_close : false,
31652 btnclick : function() {
31653 _this.uploadCancel();
31659 this.progressDialog.render(Roo.get(document.body));
31661 this.progress = new Roo.bootstrap.Progress({
31662 cls : 'roo-document-manager-progress',
31667 this.progress.render(this.progressDialog.getChildContainer());
31669 this.progressBar = new Roo.bootstrap.ProgressBar({
31670 cls : 'roo-document-manager-progress-bar',
31673 aria_valuemax : 12,
31677 this.progressBar.render(this.progress.getChildContainer());
31680 onUploaderClick : function(e)
31682 e.preventDefault();
31684 if(this.fireEvent('beforeselectfile', this) != false){
31685 this.selectorEl.dom.click();
31690 onFileSelected : function(e)
31692 e.preventDefault();
31694 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31698 Roo.each(this.selectorEl.dom.files, function(file){
31699 if(this.fireEvent('inspect', this, file) != false){
31700 this.files.push(file);
31710 this.selectorEl.dom.value = '';
31712 if(!this.files || !this.files.length){
31716 if(this.boxes > 0 && this.files.length > this.boxes){
31717 this.files = this.files.slice(0, this.boxes);
31720 this.uploader.show();
31722 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31723 this.uploader.hide();
31732 Roo.each(this.files, function(file){
31734 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31735 var f = this.renderPreview(file);
31740 if(file.type.indexOf('image') != -1){
31741 this.delegates.push(
31743 _this.process(file);
31744 }).createDelegate(this)
31752 _this.process(file);
31753 }).createDelegate(this)
31758 this.files = files;
31760 this.delegates = this.delegates.concat(docs);
31762 if(!this.delegates.length){
31767 this.progressBar.aria_valuemax = this.delegates.length;
31774 arrange : function()
31776 if(!this.delegates.length){
31777 this.progressDialog.hide();
31782 var delegate = this.delegates.shift();
31784 this.progressDialog.show();
31786 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31788 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31793 refresh : function()
31795 this.uploader.show();
31797 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31798 this.uploader.hide();
31801 Roo.isTouch ? this.closable(false) : this.closable(true);
31803 this.fireEvent('refresh', this);
31806 onRemove : function(e, el, o)
31808 e.preventDefault();
31810 this.fireEvent('remove', this, o);
31814 remove : function(o)
31818 Roo.each(this.files, function(file){
31819 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31828 this.files = files;
31835 Roo.each(this.files, function(file){
31840 file.target.remove();
31849 onClick : function(e, el, o)
31851 e.preventDefault();
31853 this.fireEvent('click', this, o);
31857 closable : function(closable)
31859 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31861 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31873 xhrOnLoad : function(xhr)
31875 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31879 if (xhr.readyState !== 4) {
31881 this.fireEvent('exception', this, xhr);
31885 var response = Roo.decode(xhr.responseText);
31887 if(!response.success){
31889 this.fireEvent('exception', this, xhr);
31893 var file = this.renderPreview(response.data);
31895 this.files.push(file);
31899 this.fireEvent('afterupload', this, xhr);
31903 xhrOnError : function(xhr)
31905 Roo.log('xhr on error');
31907 var response = Roo.decode(xhr.responseText);
31914 process : function(file)
31916 if(this.fireEvent('process', this, file) !== false){
31917 if(this.editable && file.type.indexOf('image') != -1){
31918 this.fireEvent('edit', this, file);
31922 this.uploadStart(file, false);
31929 uploadStart : function(file, crop)
31931 this.xhr = new XMLHttpRequest();
31933 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31938 file.xhr = this.xhr;
31940 this.managerEl.createChild({
31942 cls : 'roo-document-manager-loading',
31946 tooltip : file.name,
31947 cls : 'roo-document-manager-thumb',
31948 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31954 this.xhr.open(this.method, this.url, true);
31957 "Accept": "application/json",
31958 "Cache-Control": "no-cache",
31959 "X-Requested-With": "XMLHttpRequest"
31962 for (var headerName in headers) {
31963 var headerValue = headers[headerName];
31965 this.xhr.setRequestHeader(headerName, headerValue);
31971 this.xhr.onload = function()
31973 _this.xhrOnLoad(_this.xhr);
31976 this.xhr.onerror = function()
31978 _this.xhrOnError(_this.xhr);
31981 var formData = new FormData();
31983 formData.append('returnHTML', 'NO');
31986 formData.append('crop', crop);
31989 formData.append(this.paramName, file, file.name);
31996 if(this.fireEvent('prepare', this, formData, options) != false){
31998 if(options.manually){
32002 this.xhr.send(formData);
32006 this.uploadCancel();
32009 uploadCancel : function()
32015 this.delegates = [];
32017 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32024 renderPreview : function(file)
32026 if(typeof(file.target) != 'undefined' && file.target){
32030 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32032 var previewEl = this.managerEl.createChild({
32034 cls : 'roo-document-manager-preview',
32038 tooltip : file[this.toolTipName],
32039 cls : 'roo-document-manager-thumb',
32040 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32045 html : '<i class="fa fa-times-circle"></i>'
32050 var close = previewEl.select('button.close', true).first();
32052 close.on('click', this.onRemove, this, file);
32054 file.target = previewEl;
32056 var image = previewEl.select('img', true).first();
32060 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32062 image.on('click', this.onClick, this, file);
32064 this.fireEvent('previewrendered', this, file);
32070 onPreviewLoad : function(file, image)
32072 if(typeof(file.target) == 'undefined' || !file.target){
32076 var width = image.dom.naturalWidth || image.dom.width;
32077 var height = image.dom.naturalHeight || image.dom.height;
32079 if(!this.previewResize) {
32083 if(width > height){
32084 file.target.addClass('wide');
32088 file.target.addClass('tall');
32093 uploadFromSource : function(file, crop)
32095 this.xhr = new XMLHttpRequest();
32097 this.managerEl.createChild({
32099 cls : 'roo-document-manager-loading',
32103 tooltip : file.name,
32104 cls : 'roo-document-manager-thumb',
32105 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32111 this.xhr.open(this.method, this.url, true);
32114 "Accept": "application/json",
32115 "Cache-Control": "no-cache",
32116 "X-Requested-With": "XMLHttpRequest"
32119 for (var headerName in headers) {
32120 var headerValue = headers[headerName];
32122 this.xhr.setRequestHeader(headerName, headerValue);
32128 this.xhr.onload = function()
32130 _this.xhrOnLoad(_this.xhr);
32133 this.xhr.onerror = function()
32135 _this.xhrOnError(_this.xhr);
32138 var formData = new FormData();
32140 formData.append('returnHTML', 'NO');
32142 formData.append('crop', crop);
32144 if(typeof(file.filename) != 'undefined'){
32145 formData.append('filename', file.filename);
32148 if(typeof(file.mimetype) != 'undefined'){
32149 formData.append('mimetype', file.mimetype);
32154 if(this.fireEvent('prepare', this, formData) != false){
32155 this.xhr.send(formData);
32165 * @class Roo.bootstrap.DocumentViewer
32166 * @extends Roo.bootstrap.Component
32167 * Bootstrap DocumentViewer class
32168 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32169 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32172 * Create a new DocumentViewer
32173 * @param {Object} config The config object
32176 Roo.bootstrap.DocumentViewer = function(config){
32177 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32182 * Fire after initEvent
32183 * @param {Roo.bootstrap.DocumentViewer} this
32189 * @param {Roo.bootstrap.DocumentViewer} this
32194 * Fire after download button
32195 * @param {Roo.bootstrap.DocumentViewer} this
32200 * Fire after trash button
32201 * @param {Roo.bootstrap.DocumentViewer} this
32208 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32210 showDownload : true,
32214 getAutoCreate : function()
32218 cls : 'roo-document-viewer',
32222 cls : 'roo-document-viewer-body',
32226 cls : 'roo-document-viewer-thumb',
32230 cls : 'roo-document-viewer-image'
32238 cls : 'roo-document-viewer-footer',
32241 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32245 cls : 'btn-group roo-document-viewer-download',
32249 cls : 'btn btn-default',
32250 html : '<i class="fa fa-download"></i>'
32256 cls : 'btn-group roo-document-viewer-trash',
32260 cls : 'btn btn-default',
32261 html : '<i class="fa fa-trash"></i>'
32274 initEvents : function()
32276 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32277 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32279 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32280 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32282 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32283 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32285 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32286 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32288 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32289 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32291 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32292 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32294 this.bodyEl.on('click', this.onClick, this);
32295 this.downloadBtn.on('click', this.onDownload, this);
32296 this.trashBtn.on('click', this.onTrash, this);
32298 this.downloadBtn.hide();
32299 this.trashBtn.hide();
32301 if(this.showDownload){
32302 this.downloadBtn.show();
32305 if(this.showTrash){
32306 this.trashBtn.show();
32309 if(!this.showDownload && !this.showTrash) {
32310 this.footerEl.hide();
32315 initial : function()
32317 this.fireEvent('initial', this);
32321 onClick : function(e)
32323 e.preventDefault();
32325 this.fireEvent('click', this);
32328 onDownload : function(e)
32330 e.preventDefault();
32332 this.fireEvent('download', this);
32335 onTrash : function(e)
32337 e.preventDefault();
32339 this.fireEvent('trash', this);
32351 * @class Roo.bootstrap.NavProgressBar
32352 * @extends Roo.bootstrap.Component
32353 * Bootstrap NavProgressBar class
32356 * Create a new nav progress bar
32357 * @param {Object} config The config object
32360 Roo.bootstrap.NavProgressBar = function(config){
32361 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32363 this.bullets = this.bullets || [];
32365 // Roo.bootstrap.NavProgressBar.register(this);
32369 * Fires when the active item changes
32370 * @param {Roo.bootstrap.NavProgressBar} this
32371 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32372 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32379 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32384 getAutoCreate : function()
32386 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32390 cls : 'roo-navigation-bar-group',
32394 cls : 'roo-navigation-top-bar'
32398 cls : 'roo-navigation-bullets-bar',
32402 cls : 'roo-navigation-bar'
32409 cls : 'roo-navigation-bottom-bar'
32419 initEvents: function()
32424 onRender : function(ct, position)
32426 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32428 if(this.bullets.length){
32429 Roo.each(this.bullets, function(b){
32438 addItem : function(cfg)
32440 var item = new Roo.bootstrap.NavProgressItem(cfg);
32442 item.parentId = this.id;
32443 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32446 var top = new Roo.bootstrap.Element({
32448 cls : 'roo-navigation-bar-text'
32451 var bottom = new Roo.bootstrap.Element({
32453 cls : 'roo-navigation-bar-text'
32456 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32457 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32459 var topText = new Roo.bootstrap.Element({
32461 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32464 var bottomText = new Roo.bootstrap.Element({
32466 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32469 topText.onRender(top.el, null);
32470 bottomText.onRender(bottom.el, null);
32473 item.bottomEl = bottom;
32476 this.barItems.push(item);
32481 getActive : function()
32483 var active = false;
32485 Roo.each(this.barItems, function(v){
32487 if (!v.isActive()) {
32499 setActiveItem : function(item)
32503 Roo.each(this.barItems, function(v){
32504 if (v.rid == item.rid) {
32508 if (v.isActive()) {
32509 v.setActive(false);
32514 item.setActive(true);
32516 this.fireEvent('changed', this, item, prev);
32519 getBarItem: function(rid)
32523 Roo.each(this.barItems, function(e) {
32524 if (e.rid != rid) {
32535 indexOfItem : function(item)
32539 Roo.each(this.barItems, function(v, i){
32541 if (v.rid != item.rid) {
32552 setActiveNext : function()
32554 var i = this.indexOfItem(this.getActive());
32556 if (i > this.barItems.length) {
32560 this.setActiveItem(this.barItems[i+1]);
32563 setActivePrev : function()
32565 var i = this.indexOfItem(this.getActive());
32571 this.setActiveItem(this.barItems[i-1]);
32574 format : function()
32576 if(!this.barItems.length){
32580 var width = 100 / this.barItems.length;
32582 Roo.each(this.barItems, function(i){
32583 i.el.setStyle('width', width + '%');
32584 i.topEl.el.setStyle('width', width + '%');
32585 i.bottomEl.el.setStyle('width', width + '%');
32594 * Nav Progress Item
32599 * @class Roo.bootstrap.NavProgressItem
32600 * @extends Roo.bootstrap.Component
32601 * Bootstrap NavProgressItem class
32602 * @cfg {String} rid the reference id
32603 * @cfg {Boolean} active (true|false) Is item active default false
32604 * @cfg {Boolean} disabled (true|false) Is item active default false
32605 * @cfg {String} html
32606 * @cfg {String} position (top|bottom) text position default bottom
32607 * @cfg {String} icon show icon instead of number
32610 * Create a new NavProgressItem
32611 * @param {Object} config The config object
32613 Roo.bootstrap.NavProgressItem = function(config){
32614 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32619 * The raw click event for the entire grid.
32620 * @param {Roo.bootstrap.NavProgressItem} this
32621 * @param {Roo.EventObject} e
32628 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32634 position : 'bottom',
32637 getAutoCreate : function()
32639 var iconCls = 'roo-navigation-bar-item-icon';
32641 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32645 cls: 'roo-navigation-bar-item',
32655 cfg.cls += ' active';
32658 cfg.cls += ' disabled';
32664 disable : function()
32666 this.setDisabled(true);
32669 enable : function()
32671 this.setDisabled(false);
32674 initEvents: function()
32676 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32678 this.iconEl.on('click', this.onClick, this);
32681 onClick : function(e)
32683 e.preventDefault();
32689 if(this.fireEvent('click', this, e) === false){
32693 this.parent().setActiveItem(this);
32696 isActive: function ()
32698 return this.active;
32701 setActive : function(state)
32703 if(this.active == state){
32707 this.active = state;
32710 this.el.addClass('active');
32714 this.el.removeClass('active');
32719 setDisabled : function(state)
32721 if(this.disabled == state){
32725 this.disabled = state;
32728 this.el.addClass('disabled');
32732 this.el.removeClass('disabled');
32735 tooltipEl : function()
32737 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32750 * @class Roo.bootstrap.FieldLabel
32751 * @extends Roo.bootstrap.Component
32752 * Bootstrap FieldLabel class
32753 * @cfg {String} html contents of the element
32754 * @cfg {String} tag tag of the element default label
32755 * @cfg {String} cls class of the element
32756 * @cfg {String} target label target
32757 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32758 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32759 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32760 * @cfg {String} iconTooltip default "This field is required"
32761 * @cfg {String} indicatorpos (left|right) default left
32764 * Create a new FieldLabel
32765 * @param {Object} config The config object
32768 Roo.bootstrap.FieldLabel = function(config){
32769 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32774 * Fires after the field has been marked as invalid.
32775 * @param {Roo.form.FieldLabel} this
32776 * @param {String} msg The validation message
32781 * Fires after the field has been validated with no errors.
32782 * @param {Roo.form.FieldLabel} this
32788 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32795 invalidClass : 'has-warning',
32796 validClass : 'has-success',
32797 iconTooltip : 'This field is required',
32798 indicatorpos : 'left',
32800 getAutoCreate : function(){
32803 if (!this.allowBlank) {
32809 cls : 'roo-bootstrap-field-label ' + this.cls,
32814 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32815 tooltip : this.iconTooltip
32824 if(this.indicatorpos == 'right'){
32827 cls : 'roo-bootstrap-field-label ' + this.cls,
32836 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32837 tooltip : this.iconTooltip
32846 initEvents: function()
32848 Roo.bootstrap.Element.superclass.initEvents.call(this);
32850 this.indicator = this.indicatorEl();
32852 if(this.indicator){
32853 this.indicator.removeClass('visible');
32854 this.indicator.addClass('invisible');
32857 Roo.bootstrap.FieldLabel.register(this);
32860 indicatorEl : function()
32862 var indicator = this.el.select('i.roo-required-indicator',true).first();
32873 * Mark this field as valid
32875 markValid : function()
32877 if(this.indicator){
32878 this.indicator.removeClass('visible');
32879 this.indicator.addClass('invisible');
32881 if (Roo.bootstrap.version == 3) {
32882 this.el.removeClass(this.invalidClass);
32883 this.el.addClass(this.validClass);
32885 this.el.removeClass('is-invalid');
32886 this.el.addClass('is-valid');
32890 this.fireEvent('valid', this);
32894 * Mark this field as invalid
32895 * @param {String} msg The validation message
32897 markInvalid : function(msg)
32899 if(this.indicator){
32900 this.indicator.removeClass('invisible');
32901 this.indicator.addClass('visible');
32903 if (Roo.bootstrap.version == 3) {
32904 this.el.removeClass(this.validClass);
32905 this.el.addClass(this.invalidClass);
32907 this.el.removeClass('is-valid');
32908 this.el.addClass('is-invalid');
32912 this.fireEvent('invalid', this, msg);
32918 Roo.apply(Roo.bootstrap.FieldLabel, {
32923 * register a FieldLabel Group
32924 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32926 register : function(label)
32928 if(this.groups.hasOwnProperty(label.target)){
32932 this.groups[label.target] = label;
32936 * fetch a FieldLabel Group based on the target
32937 * @param {string} target
32938 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32940 get: function(target) {
32941 if (typeof(this.groups[target]) == 'undefined') {
32945 return this.groups[target] ;
32954 * page DateSplitField.
32960 * @class Roo.bootstrap.DateSplitField
32961 * @extends Roo.bootstrap.Component
32962 * Bootstrap DateSplitField class
32963 * @cfg {string} fieldLabel - the label associated
32964 * @cfg {Number} labelWidth set the width of label (0-12)
32965 * @cfg {String} labelAlign (top|left)
32966 * @cfg {Boolean} dayAllowBlank (true|false) default false
32967 * @cfg {Boolean} monthAllowBlank (true|false) default false
32968 * @cfg {Boolean} yearAllowBlank (true|false) default false
32969 * @cfg {string} dayPlaceholder
32970 * @cfg {string} monthPlaceholder
32971 * @cfg {string} yearPlaceholder
32972 * @cfg {string} dayFormat default 'd'
32973 * @cfg {string} monthFormat default 'm'
32974 * @cfg {string} yearFormat default 'Y'
32975 * @cfg {Number} labellg set the width of label (1-12)
32976 * @cfg {Number} labelmd set the width of label (1-12)
32977 * @cfg {Number} labelsm set the width of label (1-12)
32978 * @cfg {Number} labelxs set the width of label (1-12)
32982 * Create a new DateSplitField
32983 * @param {Object} config The config object
32986 Roo.bootstrap.DateSplitField = function(config){
32987 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32993 * getting the data of years
32994 * @param {Roo.bootstrap.DateSplitField} this
32995 * @param {Object} years
33000 * getting the data of days
33001 * @param {Roo.bootstrap.DateSplitField} this
33002 * @param {Object} days
33007 * Fires after the field has been marked as invalid.
33008 * @param {Roo.form.Field} this
33009 * @param {String} msg The validation message
33014 * Fires after the field has been validated with no errors.
33015 * @param {Roo.form.Field} this
33021 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33024 labelAlign : 'top',
33026 dayAllowBlank : false,
33027 monthAllowBlank : false,
33028 yearAllowBlank : false,
33029 dayPlaceholder : '',
33030 monthPlaceholder : '',
33031 yearPlaceholder : '',
33035 isFormField : true,
33041 getAutoCreate : function()
33045 cls : 'row roo-date-split-field-group',
33050 cls : 'form-hidden-field roo-date-split-field-group-value',
33056 var labelCls = 'col-md-12';
33057 var contentCls = 'col-md-4';
33059 if(this.fieldLabel){
33063 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33067 html : this.fieldLabel
33072 if(this.labelAlign == 'left'){
33074 if(this.labelWidth > 12){
33075 label.style = "width: " + this.labelWidth + 'px';
33078 if(this.labelWidth < 13 && this.labelmd == 0){
33079 this.labelmd = this.labelWidth;
33082 if(this.labellg > 0){
33083 labelCls = ' col-lg-' + this.labellg;
33084 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33087 if(this.labelmd > 0){
33088 labelCls = ' col-md-' + this.labelmd;
33089 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33092 if(this.labelsm > 0){
33093 labelCls = ' col-sm-' + this.labelsm;
33094 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33097 if(this.labelxs > 0){
33098 labelCls = ' col-xs-' + this.labelxs;
33099 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33103 label.cls += ' ' + labelCls;
33105 cfg.cn.push(label);
33108 Roo.each(['day', 'month', 'year'], function(t){
33111 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33118 inputEl: function ()
33120 return this.el.select('.roo-date-split-field-group-value', true).first();
33123 onRender : function(ct, position)
33127 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33129 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33131 this.dayField = new Roo.bootstrap.ComboBox({
33132 allowBlank : this.dayAllowBlank,
33133 alwaysQuery : true,
33134 displayField : 'value',
33137 forceSelection : true,
33139 placeholder : this.dayPlaceholder,
33140 selectOnFocus : true,
33141 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33142 triggerAction : 'all',
33144 valueField : 'value',
33145 store : new Roo.data.SimpleStore({
33146 data : (function() {
33148 _this.fireEvent('days', _this, days);
33151 fields : [ 'value' ]
33154 select : function (_self, record, index)
33156 _this.setValue(_this.getValue());
33161 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33163 this.monthField = new Roo.bootstrap.MonthField({
33164 after : '<i class=\"fa fa-calendar\"></i>',
33165 allowBlank : this.monthAllowBlank,
33166 placeholder : this.monthPlaceholder,
33169 render : function (_self)
33171 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33172 e.preventDefault();
33176 select : function (_self, oldvalue, newvalue)
33178 _this.setValue(_this.getValue());
33183 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33185 this.yearField = new Roo.bootstrap.ComboBox({
33186 allowBlank : this.yearAllowBlank,
33187 alwaysQuery : true,
33188 displayField : 'value',
33191 forceSelection : true,
33193 placeholder : this.yearPlaceholder,
33194 selectOnFocus : true,
33195 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33196 triggerAction : 'all',
33198 valueField : 'value',
33199 store : new Roo.data.SimpleStore({
33200 data : (function() {
33202 _this.fireEvent('years', _this, years);
33205 fields : [ 'value' ]
33208 select : function (_self, record, index)
33210 _this.setValue(_this.getValue());
33215 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33218 setValue : function(v, format)
33220 this.inputEl.dom.value = v;
33222 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33224 var d = Date.parseDate(v, f);
33231 this.setDay(d.format(this.dayFormat));
33232 this.setMonth(d.format(this.monthFormat));
33233 this.setYear(d.format(this.yearFormat));
33240 setDay : function(v)
33242 this.dayField.setValue(v);
33243 this.inputEl.dom.value = this.getValue();
33248 setMonth : function(v)
33250 this.monthField.setValue(v, true);
33251 this.inputEl.dom.value = this.getValue();
33256 setYear : function(v)
33258 this.yearField.setValue(v);
33259 this.inputEl.dom.value = this.getValue();
33264 getDay : function()
33266 return this.dayField.getValue();
33269 getMonth : function()
33271 return this.monthField.getValue();
33274 getYear : function()
33276 return this.yearField.getValue();
33279 getValue : function()
33281 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33283 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33293 this.inputEl.dom.value = '';
33298 validate : function()
33300 var d = this.dayField.validate();
33301 var m = this.monthField.validate();
33302 var y = this.yearField.validate();
33307 (!this.dayAllowBlank && !d) ||
33308 (!this.monthAllowBlank && !m) ||
33309 (!this.yearAllowBlank && !y)
33314 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33323 this.markInvalid();
33328 markValid : function()
33331 var label = this.el.select('label', true).first();
33332 var icon = this.el.select('i.fa-star', true).first();
33338 this.fireEvent('valid', this);
33342 * Mark this field as invalid
33343 * @param {String} msg The validation message
33345 markInvalid : function(msg)
33348 var label = this.el.select('label', true).first();
33349 var icon = this.el.select('i.fa-star', true).first();
33351 if(label && !icon){
33352 this.el.select('.roo-date-split-field-label', true).createChild({
33354 cls : 'text-danger fa fa-lg fa-star',
33355 tooltip : 'This field is required',
33356 style : 'margin-right:5px;'
33360 this.fireEvent('invalid', this, msg);
33363 clearInvalid : function()
33365 var label = this.el.select('label', true).first();
33366 var icon = this.el.select('i.fa-star', true).first();
33372 this.fireEvent('valid', this);
33375 getName: function()
33385 * http://masonry.desandro.com
33387 * The idea is to render all the bricks based on vertical width...
33389 * The original code extends 'outlayer' - we might need to use that....
33395 * @class Roo.bootstrap.LayoutMasonry
33396 * @extends Roo.bootstrap.Component
33397 * Bootstrap Layout Masonry class
33400 * Create a new Element
33401 * @param {Object} config The config object
33404 Roo.bootstrap.LayoutMasonry = function(config){
33406 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33410 Roo.bootstrap.LayoutMasonry.register(this);
33416 * Fire after layout the items
33417 * @param {Roo.bootstrap.LayoutMasonry} this
33418 * @param {Roo.EventObject} e
33425 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33428 * @cfg {Boolean} isLayoutInstant = no animation?
33430 isLayoutInstant : false, // needed?
33433 * @cfg {Number} boxWidth width of the columns
33438 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33443 * @cfg {Number} padWidth padding below box..
33448 * @cfg {Number} gutter gutter width..
33453 * @cfg {Number} maxCols maximum number of columns
33459 * @cfg {Boolean} isAutoInitial defalut true
33461 isAutoInitial : true,
33466 * @cfg {Boolean} isHorizontal defalut false
33468 isHorizontal : false,
33470 currentSize : null,
33476 bricks: null, //CompositeElement
33480 _isLayoutInited : false,
33482 // isAlternative : false, // only use for vertical layout...
33485 * @cfg {Number} alternativePadWidth padding below box..
33487 alternativePadWidth : 50,
33489 selectedBrick : [],
33491 getAutoCreate : function(){
33493 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33497 cls: 'blog-masonary-wrapper ' + this.cls,
33499 cls : 'mas-boxes masonary'
33506 getChildContainer: function( )
33508 if (this.boxesEl) {
33509 return this.boxesEl;
33512 this.boxesEl = this.el.select('.mas-boxes').first();
33514 return this.boxesEl;
33518 initEvents : function()
33522 if(this.isAutoInitial){
33523 Roo.log('hook children rendered');
33524 this.on('childrenrendered', function() {
33525 Roo.log('children rendered');
33531 initial : function()
33533 this.selectedBrick = [];
33535 this.currentSize = this.el.getBox(true);
33537 Roo.EventManager.onWindowResize(this.resize, this);
33539 if(!this.isAutoInitial){
33547 //this.layout.defer(500,this);
33551 resize : function()
33553 var cs = this.el.getBox(true);
33556 this.currentSize.width == cs.width &&
33557 this.currentSize.x == cs.x &&
33558 this.currentSize.height == cs.height &&
33559 this.currentSize.y == cs.y
33561 Roo.log("no change in with or X or Y");
33565 this.currentSize = cs;
33571 layout : function()
33573 this._resetLayout();
33575 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33577 this.layoutItems( isInstant );
33579 this._isLayoutInited = true;
33581 this.fireEvent('layout', this);
33585 _resetLayout : function()
33587 if(this.isHorizontal){
33588 this.horizontalMeasureColumns();
33592 this.verticalMeasureColumns();
33596 verticalMeasureColumns : function()
33598 this.getContainerWidth();
33600 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33601 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33605 var boxWidth = this.boxWidth + this.padWidth;
33607 if(this.containerWidth < this.boxWidth){
33608 boxWidth = this.containerWidth
33611 var containerWidth = this.containerWidth;
33613 var cols = Math.floor(containerWidth / boxWidth);
33615 this.cols = Math.max( cols, 1 );
33617 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33619 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33621 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33623 this.colWidth = boxWidth + avail - this.padWidth;
33625 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33626 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33629 horizontalMeasureColumns : function()
33631 this.getContainerWidth();
33633 var boxWidth = this.boxWidth;
33635 if(this.containerWidth < boxWidth){
33636 boxWidth = this.containerWidth;
33639 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33641 this.el.setHeight(boxWidth);
33645 getContainerWidth : function()
33647 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33650 layoutItems : function( isInstant )
33652 Roo.log(this.bricks);
33654 var items = Roo.apply([], this.bricks);
33656 if(this.isHorizontal){
33657 this._horizontalLayoutItems( items , isInstant );
33661 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33662 // this._verticalAlternativeLayoutItems( items , isInstant );
33666 this._verticalLayoutItems( items , isInstant );
33670 _verticalLayoutItems : function ( items , isInstant)
33672 if ( !items || !items.length ) {
33677 ['xs', 'xs', 'xs', 'tall'],
33678 ['xs', 'xs', 'tall'],
33679 ['xs', 'xs', 'sm'],
33680 ['xs', 'xs', 'xs'],
33686 ['sm', 'xs', 'xs'],
33690 ['tall', 'xs', 'xs', 'xs'],
33691 ['tall', 'xs', 'xs'],
33703 Roo.each(items, function(item, k){
33705 switch (item.size) {
33706 // these layouts take up a full box,
33717 boxes.push([item]);
33740 var filterPattern = function(box, length)
33748 var pattern = box.slice(0, length);
33752 Roo.each(pattern, function(i){
33753 format.push(i.size);
33756 Roo.each(standard, function(s){
33758 if(String(s) != String(format)){
33767 if(!match && length == 1){
33772 filterPattern(box, length - 1);
33776 queue.push(pattern);
33778 box = box.slice(length, box.length);
33780 filterPattern(box, 4);
33786 Roo.each(boxes, function(box, k){
33792 if(box.length == 1){
33797 filterPattern(box, 4);
33801 this._processVerticalLayoutQueue( queue, isInstant );
33805 // _verticalAlternativeLayoutItems : function( items , isInstant )
33807 // if ( !items || !items.length ) {
33811 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33815 _horizontalLayoutItems : function ( items , isInstant)
33817 if ( !items || !items.length || items.length < 3) {
33823 var eItems = items.slice(0, 3);
33825 items = items.slice(3, items.length);
33828 ['xs', 'xs', 'xs', 'wide'],
33829 ['xs', 'xs', 'wide'],
33830 ['xs', 'xs', 'sm'],
33831 ['xs', 'xs', 'xs'],
33837 ['sm', 'xs', 'xs'],
33841 ['wide', 'xs', 'xs', 'xs'],
33842 ['wide', 'xs', 'xs'],
33855 Roo.each(items, function(item, k){
33857 switch (item.size) {
33868 boxes.push([item]);
33892 var filterPattern = function(box, length)
33900 var pattern = box.slice(0, length);
33904 Roo.each(pattern, function(i){
33905 format.push(i.size);
33908 Roo.each(standard, function(s){
33910 if(String(s) != String(format)){
33919 if(!match && length == 1){
33924 filterPattern(box, length - 1);
33928 queue.push(pattern);
33930 box = box.slice(length, box.length);
33932 filterPattern(box, 4);
33938 Roo.each(boxes, function(box, k){
33944 if(box.length == 1){
33949 filterPattern(box, 4);
33956 var pos = this.el.getBox(true);
33960 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33962 var hit_end = false;
33964 Roo.each(queue, function(box){
33968 Roo.each(box, function(b){
33970 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33980 Roo.each(box, function(b){
33982 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33985 mx = Math.max(mx, b.x);
33989 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33993 Roo.each(box, function(b){
33995 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34009 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34012 /** Sets position of item in DOM
34013 * @param {Element} item
34014 * @param {Number} x - horizontal position
34015 * @param {Number} y - vertical position
34016 * @param {Boolean} isInstant - disables transitions
34018 _processVerticalLayoutQueue : function( queue, isInstant )
34020 var pos = this.el.getBox(true);
34025 for (var i = 0; i < this.cols; i++){
34029 Roo.each(queue, function(box, k){
34031 var col = k % this.cols;
34033 Roo.each(box, function(b,kk){
34035 b.el.position('absolute');
34037 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34038 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34040 if(b.size == 'md-left' || b.size == 'md-right'){
34041 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34042 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34045 b.el.setWidth(width);
34046 b.el.setHeight(height);
34048 b.el.select('iframe',true).setSize(width,height);
34052 for (var i = 0; i < this.cols; i++){
34054 if(maxY[i] < maxY[col]){
34059 col = Math.min(col, i);
34063 x = pos.x + col * (this.colWidth + this.padWidth);
34067 var positions = [];
34069 switch (box.length){
34071 positions = this.getVerticalOneBoxColPositions(x, y, box);
34074 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34077 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34080 positions = this.getVerticalFourBoxColPositions(x, y, box);
34086 Roo.each(box, function(b,kk){
34088 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34090 var sz = b.el.getSize();
34092 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34100 for (var i = 0; i < this.cols; i++){
34101 mY = Math.max(mY, maxY[i]);
34104 this.el.setHeight(mY - pos.y);
34108 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34110 // var pos = this.el.getBox(true);
34113 // var maxX = pos.right;
34115 // var maxHeight = 0;
34117 // Roo.each(items, function(item, k){
34121 // item.el.position('absolute');
34123 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34125 // item.el.setWidth(width);
34127 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34129 // item.el.setHeight(height);
34132 // item.el.setXY([x, y], isInstant ? false : true);
34134 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34137 // y = y + height + this.alternativePadWidth;
34139 // maxHeight = maxHeight + height + this.alternativePadWidth;
34143 // this.el.setHeight(maxHeight);
34147 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34149 var pos = this.el.getBox(true);
34154 var maxX = pos.right;
34156 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34158 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34160 Roo.each(queue, function(box, k){
34162 Roo.each(box, function(b, kk){
34164 b.el.position('absolute');
34166 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34167 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34169 if(b.size == 'md-left' || b.size == 'md-right'){
34170 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34171 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34174 b.el.setWidth(width);
34175 b.el.setHeight(height);
34183 var positions = [];
34185 switch (box.length){
34187 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34190 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34193 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34196 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34202 Roo.each(box, function(b,kk){
34204 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34206 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34214 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34216 Roo.each(eItems, function(b,k){
34218 b.size = (k == 0) ? 'sm' : 'xs';
34219 b.x = (k == 0) ? 2 : 1;
34220 b.y = (k == 0) ? 2 : 1;
34222 b.el.position('absolute');
34224 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34226 b.el.setWidth(width);
34228 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34230 b.el.setHeight(height);
34234 var positions = [];
34237 x : maxX - this.unitWidth * 2 - this.gutter,
34242 x : maxX - this.unitWidth,
34243 y : minY + (this.unitWidth + this.gutter) * 2
34247 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34251 Roo.each(eItems, function(b,k){
34253 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34259 getVerticalOneBoxColPositions : function(x, y, box)
34263 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34265 if(box[0].size == 'md-left'){
34269 if(box[0].size == 'md-right'){
34274 x : x + (this.unitWidth + this.gutter) * rand,
34281 getVerticalTwoBoxColPositions : function(x, y, box)
34285 if(box[0].size == 'xs'){
34289 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34293 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34307 x : x + (this.unitWidth + this.gutter) * 2,
34308 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34315 getVerticalThreeBoxColPositions : function(x, y, box)
34319 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34327 x : x + (this.unitWidth + this.gutter) * 1,
34332 x : x + (this.unitWidth + this.gutter) * 2,
34340 if(box[0].size == 'xs' && box[1].size == 'xs'){
34349 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34353 x : x + (this.unitWidth + this.gutter) * 1,
34367 x : x + (this.unitWidth + this.gutter) * 2,
34372 x : x + (this.unitWidth + this.gutter) * 2,
34373 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34380 getVerticalFourBoxColPositions : function(x, y, box)
34384 if(box[0].size == 'xs'){
34393 y : y + (this.unitHeight + this.gutter) * 1
34398 y : y + (this.unitHeight + this.gutter) * 2
34402 x : x + (this.unitWidth + this.gutter) * 1,
34416 x : x + (this.unitWidth + this.gutter) * 2,
34421 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34422 y : y + (this.unitHeight + this.gutter) * 1
34426 x : x + (this.unitWidth + this.gutter) * 2,
34427 y : y + (this.unitWidth + this.gutter) * 2
34434 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34438 if(box[0].size == 'md-left'){
34440 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34447 if(box[0].size == 'md-right'){
34449 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34450 y : minY + (this.unitWidth + this.gutter) * 1
34456 var rand = Math.floor(Math.random() * (4 - box[0].y));
34459 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34460 y : minY + (this.unitWidth + this.gutter) * rand
34467 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34471 if(box[0].size == 'xs'){
34474 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34479 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34480 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34488 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34493 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34494 y : minY + (this.unitWidth + this.gutter) * 2
34501 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34505 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34508 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34513 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34514 y : minY + (this.unitWidth + this.gutter) * 1
34518 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34519 y : minY + (this.unitWidth + this.gutter) * 2
34526 if(box[0].size == 'xs' && box[1].size == 'xs'){
34529 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34534 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34539 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34540 y : minY + (this.unitWidth + this.gutter) * 1
34548 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34553 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34554 y : minY + (this.unitWidth + this.gutter) * 2
34558 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34559 y : minY + (this.unitWidth + this.gutter) * 2
34566 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34570 if(box[0].size == 'xs'){
34573 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34578 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34583 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),
34588 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34589 y : minY + (this.unitWidth + this.gutter) * 1
34597 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34602 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34603 y : minY + (this.unitWidth + this.gutter) * 2
34607 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34608 y : minY + (this.unitWidth + this.gutter) * 2
34612 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),
34613 y : minY + (this.unitWidth + this.gutter) * 2
34621 * remove a Masonry Brick
34622 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34624 removeBrick : function(brick_id)
34630 for (var i = 0; i<this.bricks.length; i++) {
34631 if (this.bricks[i].id == brick_id) {
34632 this.bricks.splice(i,1);
34633 this.el.dom.removeChild(Roo.get(brick_id).dom);
34640 * adds a Masonry Brick
34641 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34643 addBrick : function(cfg)
34645 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34646 //this.register(cn);
34647 cn.parentId = this.id;
34648 cn.render(this.el);
34653 * register a Masonry Brick
34654 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34657 register : function(brick)
34659 this.bricks.push(brick);
34660 brick.masonryId = this.id;
34664 * clear all the Masonry Brick
34666 clearAll : function()
34669 //this.getChildContainer().dom.innerHTML = "";
34670 this.el.dom.innerHTML = '';
34673 getSelected : function()
34675 if (!this.selectedBrick) {
34679 return this.selectedBrick;
34683 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34687 * register a Masonry Layout
34688 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34691 register : function(layout)
34693 this.groups[layout.id] = layout;
34696 * fetch a Masonry Layout based on the masonry layout ID
34697 * @param {string} the masonry layout to add
34698 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34701 get: function(layout_id) {
34702 if (typeof(this.groups[layout_id]) == 'undefined') {
34705 return this.groups[layout_id] ;
34717 * http://masonry.desandro.com
34719 * The idea is to render all the bricks based on vertical width...
34721 * The original code extends 'outlayer' - we might need to use that....
34727 * @class Roo.bootstrap.LayoutMasonryAuto
34728 * @extends Roo.bootstrap.Component
34729 * Bootstrap Layout Masonry class
34732 * Create a new Element
34733 * @param {Object} config The config object
34736 Roo.bootstrap.LayoutMasonryAuto = function(config){
34737 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34740 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34743 * @cfg {Boolean} isFitWidth - resize the width..
34745 isFitWidth : false, // options..
34747 * @cfg {Boolean} isOriginLeft = left align?
34749 isOriginLeft : true,
34751 * @cfg {Boolean} isOriginTop = top align?
34753 isOriginTop : false,
34755 * @cfg {Boolean} isLayoutInstant = no animation?
34757 isLayoutInstant : false, // needed?
34759 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34761 isResizingContainer : true,
34763 * @cfg {Number} columnWidth width of the columns
34769 * @cfg {Number} maxCols maximum number of columns
34774 * @cfg {Number} padHeight padding below box..
34780 * @cfg {Boolean} isAutoInitial defalut true
34783 isAutoInitial : true,
34789 initialColumnWidth : 0,
34790 currentSize : null,
34792 colYs : null, // array.
34799 bricks: null, //CompositeElement
34800 cols : 0, // array?
34801 // element : null, // wrapped now this.el
34802 _isLayoutInited : null,
34805 getAutoCreate : function(){
34809 cls: 'blog-masonary-wrapper ' + this.cls,
34811 cls : 'mas-boxes masonary'
34818 getChildContainer: function( )
34820 if (this.boxesEl) {
34821 return this.boxesEl;
34824 this.boxesEl = this.el.select('.mas-boxes').first();
34826 return this.boxesEl;
34830 initEvents : function()
34834 if(this.isAutoInitial){
34835 Roo.log('hook children rendered');
34836 this.on('childrenrendered', function() {
34837 Roo.log('children rendered');
34844 initial : function()
34846 this.reloadItems();
34848 this.currentSize = this.el.getBox(true);
34850 /// was window resize... - let's see if this works..
34851 Roo.EventManager.onWindowResize(this.resize, this);
34853 if(!this.isAutoInitial){
34858 this.layout.defer(500,this);
34861 reloadItems: function()
34863 this.bricks = this.el.select('.masonry-brick', true);
34865 this.bricks.each(function(b) {
34866 //Roo.log(b.getSize());
34867 if (!b.attr('originalwidth')) {
34868 b.attr('originalwidth', b.getSize().width);
34873 Roo.log(this.bricks.elements.length);
34876 resize : function()
34879 var cs = this.el.getBox(true);
34881 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34882 Roo.log("no change in with or X");
34885 this.currentSize = cs;
34889 layout : function()
34892 this._resetLayout();
34893 //this._manageStamps();
34895 // don't animate first layout
34896 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34897 this.layoutItems( isInstant );
34899 // flag for initalized
34900 this._isLayoutInited = true;
34903 layoutItems : function( isInstant )
34905 //var items = this._getItemsForLayout( this.items );
34906 // original code supports filtering layout items.. we just ignore it..
34908 this._layoutItems( this.bricks , isInstant );
34910 this._postLayout();
34912 _layoutItems : function ( items , isInstant)
34914 //this.fireEvent( 'layout', this, items );
34917 if ( !items || !items.elements.length ) {
34918 // no items, emit event with empty array
34923 items.each(function(item) {
34924 Roo.log("layout item");
34926 // get x/y object from method
34927 var position = this._getItemLayoutPosition( item );
34929 position.item = item;
34930 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34931 queue.push( position );
34934 this._processLayoutQueue( queue );
34936 /** Sets position of item in DOM
34937 * @param {Element} item
34938 * @param {Number} x - horizontal position
34939 * @param {Number} y - vertical position
34940 * @param {Boolean} isInstant - disables transitions
34942 _processLayoutQueue : function( queue )
34944 for ( var i=0, len = queue.length; i < len; i++ ) {
34945 var obj = queue[i];
34946 obj.item.position('absolute');
34947 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34953 * Any logic you want to do after each layout,
34954 * i.e. size the container
34956 _postLayout : function()
34958 this.resizeContainer();
34961 resizeContainer : function()
34963 if ( !this.isResizingContainer ) {
34966 var size = this._getContainerSize();
34968 this.el.setSize(size.width,size.height);
34969 this.boxesEl.setSize(size.width,size.height);
34975 _resetLayout : function()
34977 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34978 this.colWidth = this.el.getWidth();
34979 //this.gutter = this.el.getWidth();
34981 this.measureColumns();
34987 this.colYs.push( 0 );
34993 measureColumns : function()
34995 this.getContainerWidth();
34996 // if columnWidth is 0, default to outerWidth of first item
34997 if ( !this.columnWidth ) {
34998 var firstItem = this.bricks.first();
34999 Roo.log(firstItem);
35000 this.columnWidth = this.containerWidth;
35001 if (firstItem && firstItem.attr('originalwidth') ) {
35002 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35004 // columnWidth fall back to item of first element
35005 Roo.log("set column width?");
35006 this.initialColumnWidth = this.columnWidth ;
35008 // if first elem has no width, default to size of container
35013 if (this.initialColumnWidth) {
35014 this.columnWidth = this.initialColumnWidth;
35019 // column width is fixed at the top - however if container width get's smaller we should
35022 // this bit calcs how man columns..
35024 var columnWidth = this.columnWidth += this.gutter;
35026 // calculate columns
35027 var containerWidth = this.containerWidth + this.gutter;
35029 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35030 // fix rounding errors, typically with gutters
35031 var excess = columnWidth - containerWidth % columnWidth;
35034 // if overshoot is less than a pixel, round up, otherwise floor it
35035 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35036 cols = Math[ mathMethod ]( cols );
35037 this.cols = Math.max( cols, 1 );
35038 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35040 // padding positioning..
35041 var totalColWidth = this.cols * this.columnWidth;
35042 var padavail = this.containerWidth - totalColWidth;
35043 // so for 2 columns - we need 3 'pads'
35045 var padNeeded = (1+this.cols) * this.padWidth;
35047 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35049 this.columnWidth += padExtra
35050 //this.padWidth = Math.floor(padavail / ( this.cols));
35052 // adjust colum width so that padding is fixed??
35054 // we have 3 columns ... total = width * 3
35055 // we have X left over... that should be used by
35057 //if (this.expandC) {
35065 getContainerWidth : function()
35067 /* // container is parent if fit width
35068 var container = this.isFitWidth ? this.element.parentNode : this.element;
35069 // check that this.size and size are there
35070 // IE8 triggers resize on body size change, so they might not be
35072 var size = getSize( container ); //FIXME
35073 this.containerWidth = size && size.innerWidth; //FIXME
35076 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35080 _getItemLayoutPosition : function( item ) // what is item?
35082 // we resize the item to our columnWidth..
35084 item.setWidth(this.columnWidth);
35085 item.autoBoxAdjust = false;
35087 var sz = item.getSize();
35089 // how many columns does this brick span
35090 var remainder = this.containerWidth % this.columnWidth;
35092 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35093 // round if off by 1 pixel, otherwise use ceil
35094 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35095 colSpan = Math.min( colSpan, this.cols );
35097 // normally this should be '1' as we dont' currently allow multi width columns..
35099 var colGroup = this._getColGroup( colSpan );
35100 // get the minimum Y value from the columns
35101 var minimumY = Math.min.apply( Math, colGroup );
35102 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35104 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35106 // position the brick
35108 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35109 y: this.currentSize.y + minimumY + this.padHeight
35113 // apply setHeight to necessary columns
35114 var setHeight = minimumY + sz.height + this.padHeight;
35115 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35117 var setSpan = this.cols + 1 - colGroup.length;
35118 for ( var i = 0; i < setSpan; i++ ) {
35119 this.colYs[ shortColIndex + i ] = setHeight ;
35126 * @param {Number} colSpan - number of columns the element spans
35127 * @returns {Array} colGroup
35129 _getColGroup : function( colSpan )
35131 if ( colSpan < 2 ) {
35132 // if brick spans only one column, use all the column Ys
35137 // how many different places could this brick fit horizontally
35138 var groupCount = this.cols + 1 - colSpan;
35139 // for each group potential horizontal position
35140 for ( var i = 0; i < groupCount; i++ ) {
35141 // make an array of colY values for that one group
35142 var groupColYs = this.colYs.slice( i, i + colSpan );
35143 // and get the max value of the array
35144 colGroup[i] = Math.max.apply( Math, groupColYs );
35149 _manageStamp : function( stamp )
35151 var stampSize = stamp.getSize();
35152 var offset = stamp.getBox();
35153 // get the columns that this stamp affects
35154 var firstX = this.isOriginLeft ? offset.x : offset.right;
35155 var lastX = firstX + stampSize.width;
35156 var firstCol = Math.floor( firstX / this.columnWidth );
35157 firstCol = Math.max( 0, firstCol );
35159 var lastCol = Math.floor( lastX / this.columnWidth );
35160 // lastCol should not go over if multiple of columnWidth #425
35161 lastCol -= lastX % this.columnWidth ? 0 : 1;
35162 lastCol = Math.min( this.cols - 1, lastCol );
35164 // set colYs to bottom of the stamp
35165 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35168 for ( var i = firstCol; i <= lastCol; i++ ) {
35169 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35174 _getContainerSize : function()
35176 this.maxY = Math.max.apply( Math, this.colYs );
35181 if ( this.isFitWidth ) {
35182 size.width = this._getContainerFitWidth();
35188 _getContainerFitWidth : function()
35190 var unusedCols = 0;
35191 // count unused columns
35194 if ( this.colYs[i] !== 0 ) {
35199 // fit container to columns that have been used
35200 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35203 needsResizeLayout : function()
35205 var previousWidth = this.containerWidth;
35206 this.getContainerWidth();
35207 return previousWidth !== this.containerWidth;
35222 * @class Roo.bootstrap.MasonryBrick
35223 * @extends Roo.bootstrap.Component
35224 * Bootstrap MasonryBrick class
35227 * Create a new MasonryBrick
35228 * @param {Object} config The config object
35231 Roo.bootstrap.MasonryBrick = function(config){
35233 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35235 Roo.bootstrap.MasonryBrick.register(this);
35241 * When a MasonryBrick is clcik
35242 * @param {Roo.bootstrap.MasonryBrick} this
35243 * @param {Roo.EventObject} e
35249 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35252 * @cfg {String} title
35256 * @cfg {String} html
35260 * @cfg {String} bgimage
35264 * @cfg {String} videourl
35268 * @cfg {String} cls
35272 * @cfg {String} href
35276 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35281 * @cfg {String} placetitle (center|bottom)
35286 * @cfg {Boolean} isFitContainer defalut true
35288 isFitContainer : true,
35291 * @cfg {Boolean} preventDefault defalut false
35293 preventDefault : false,
35296 * @cfg {Boolean} inverse defalut false
35298 maskInverse : false,
35300 getAutoCreate : function()
35302 if(!this.isFitContainer){
35303 return this.getSplitAutoCreate();
35306 var cls = 'masonry-brick masonry-brick-full';
35308 if(this.href.length){
35309 cls += ' masonry-brick-link';
35312 if(this.bgimage.length){
35313 cls += ' masonry-brick-image';
35316 if(this.maskInverse){
35317 cls += ' mask-inverse';
35320 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35321 cls += ' enable-mask';
35325 cls += ' masonry-' + this.size + '-brick';
35328 if(this.placetitle.length){
35330 switch (this.placetitle) {
35332 cls += ' masonry-center-title';
35335 cls += ' masonry-bottom-title';
35342 if(!this.html.length && !this.bgimage.length){
35343 cls += ' masonry-center-title';
35346 if(!this.html.length && this.bgimage.length){
35347 cls += ' masonry-bottom-title';
35352 cls += ' ' + this.cls;
35356 tag: (this.href.length) ? 'a' : 'div',
35361 cls: 'masonry-brick-mask'
35365 cls: 'masonry-brick-paragraph',
35371 if(this.href.length){
35372 cfg.href = this.href;
35375 var cn = cfg.cn[1].cn;
35377 if(this.title.length){
35380 cls: 'masonry-brick-title',
35385 if(this.html.length){
35388 cls: 'masonry-brick-text',
35393 if (!this.title.length && !this.html.length) {
35394 cfg.cn[1].cls += ' hide';
35397 if(this.bgimage.length){
35400 cls: 'masonry-brick-image-view',
35405 if(this.videourl.length){
35406 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35407 // youtube support only?
35410 cls: 'masonry-brick-image-view',
35413 allowfullscreen : true
35421 getSplitAutoCreate : function()
35423 var cls = 'masonry-brick masonry-brick-split';
35425 if(this.href.length){
35426 cls += ' masonry-brick-link';
35429 if(this.bgimage.length){
35430 cls += ' masonry-brick-image';
35434 cls += ' masonry-' + this.size + '-brick';
35437 switch (this.placetitle) {
35439 cls += ' masonry-center-title';
35442 cls += ' masonry-bottom-title';
35445 if(!this.bgimage.length){
35446 cls += ' masonry-center-title';
35449 if(this.bgimage.length){
35450 cls += ' masonry-bottom-title';
35456 cls += ' ' + this.cls;
35460 tag: (this.href.length) ? 'a' : 'div',
35465 cls: 'masonry-brick-split-head',
35469 cls: 'masonry-brick-paragraph',
35476 cls: 'masonry-brick-split-body',
35482 if(this.href.length){
35483 cfg.href = this.href;
35486 if(this.title.length){
35487 cfg.cn[0].cn[0].cn.push({
35489 cls: 'masonry-brick-title',
35494 if(this.html.length){
35495 cfg.cn[1].cn.push({
35497 cls: 'masonry-brick-text',
35502 if(this.bgimage.length){
35503 cfg.cn[0].cn.push({
35505 cls: 'masonry-brick-image-view',
35510 if(this.videourl.length){
35511 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35512 // youtube support only?
35513 cfg.cn[0].cn.cn.push({
35515 cls: 'masonry-brick-image-view',
35518 allowfullscreen : true
35525 initEvents: function()
35527 switch (this.size) {
35560 this.el.on('touchstart', this.onTouchStart, this);
35561 this.el.on('touchmove', this.onTouchMove, this);
35562 this.el.on('touchend', this.onTouchEnd, this);
35563 this.el.on('contextmenu', this.onContextMenu, this);
35565 this.el.on('mouseenter' ,this.enter, this);
35566 this.el.on('mouseleave', this.leave, this);
35567 this.el.on('click', this.onClick, this);
35570 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35571 this.parent().bricks.push(this);
35576 onClick: function(e, el)
35578 var time = this.endTimer - this.startTimer;
35579 // Roo.log(e.preventDefault());
35582 e.preventDefault();
35587 if(!this.preventDefault){
35591 e.preventDefault();
35593 if (this.activeClass != '') {
35594 this.selectBrick();
35597 this.fireEvent('click', this, e);
35600 enter: function(e, el)
35602 e.preventDefault();
35604 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35608 if(this.bgimage.length && this.html.length){
35609 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35613 leave: function(e, el)
35615 e.preventDefault();
35617 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35621 if(this.bgimage.length && this.html.length){
35622 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35626 onTouchStart: function(e, el)
35628 // e.preventDefault();
35630 this.touchmoved = false;
35632 if(!this.isFitContainer){
35636 if(!this.bgimage.length || !this.html.length){
35640 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35642 this.timer = new Date().getTime();
35646 onTouchMove: function(e, el)
35648 this.touchmoved = true;
35651 onContextMenu : function(e,el)
35653 e.preventDefault();
35654 e.stopPropagation();
35658 onTouchEnd: function(e, el)
35660 // e.preventDefault();
35662 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35669 if(!this.bgimage.length || !this.html.length){
35671 if(this.href.length){
35672 window.location.href = this.href;
35678 if(!this.isFitContainer){
35682 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35684 window.location.href = this.href;
35687 //selection on single brick only
35688 selectBrick : function() {
35690 if (!this.parentId) {
35694 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35695 var index = m.selectedBrick.indexOf(this.id);
35698 m.selectedBrick.splice(index,1);
35699 this.el.removeClass(this.activeClass);
35703 for(var i = 0; i < m.selectedBrick.length; i++) {
35704 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35705 b.el.removeClass(b.activeClass);
35708 m.selectedBrick = [];
35710 m.selectedBrick.push(this.id);
35711 this.el.addClass(this.activeClass);
35715 isSelected : function(){
35716 return this.el.hasClass(this.activeClass);
35721 Roo.apply(Roo.bootstrap.MasonryBrick, {
35724 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35726 * register a Masonry Brick
35727 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35730 register : function(brick)
35732 //this.groups[brick.id] = brick;
35733 this.groups.add(brick.id, brick);
35736 * fetch a masonry brick based on the masonry brick ID
35737 * @param {string} the masonry brick to add
35738 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35741 get: function(brick_id)
35743 // if (typeof(this.groups[brick_id]) == 'undefined') {
35746 // return this.groups[brick_id] ;
35748 if(this.groups.key(brick_id)) {
35749 return this.groups.key(brick_id);
35767 * @class Roo.bootstrap.Brick
35768 * @extends Roo.bootstrap.Component
35769 * Bootstrap Brick class
35772 * Create a new Brick
35773 * @param {Object} config The config object
35776 Roo.bootstrap.Brick = function(config){
35777 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35783 * When a Brick is click
35784 * @param {Roo.bootstrap.Brick} this
35785 * @param {Roo.EventObject} e
35791 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35794 * @cfg {String} title
35798 * @cfg {String} html
35802 * @cfg {String} bgimage
35806 * @cfg {String} cls
35810 * @cfg {String} href
35814 * @cfg {String} video
35818 * @cfg {Boolean} square
35822 getAutoCreate : function()
35824 var cls = 'roo-brick';
35826 if(this.href.length){
35827 cls += ' roo-brick-link';
35830 if(this.bgimage.length){
35831 cls += ' roo-brick-image';
35834 if(!this.html.length && !this.bgimage.length){
35835 cls += ' roo-brick-center-title';
35838 if(!this.html.length && this.bgimage.length){
35839 cls += ' roo-brick-bottom-title';
35843 cls += ' ' + this.cls;
35847 tag: (this.href.length) ? 'a' : 'div',
35852 cls: 'roo-brick-paragraph',
35858 if(this.href.length){
35859 cfg.href = this.href;
35862 var cn = cfg.cn[0].cn;
35864 if(this.title.length){
35867 cls: 'roo-brick-title',
35872 if(this.html.length){
35875 cls: 'roo-brick-text',
35882 if(this.bgimage.length){
35885 cls: 'roo-brick-image-view',
35893 initEvents: function()
35895 if(this.title.length || this.html.length){
35896 this.el.on('mouseenter' ,this.enter, this);
35897 this.el.on('mouseleave', this.leave, this);
35900 Roo.EventManager.onWindowResize(this.resize, this);
35902 if(this.bgimage.length){
35903 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35904 this.imageEl.on('load', this.onImageLoad, this);
35911 onImageLoad : function()
35916 resize : function()
35918 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35920 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35922 if(this.bgimage.length){
35923 var image = this.el.select('.roo-brick-image-view', true).first();
35925 image.setWidth(paragraph.getWidth());
35928 image.setHeight(paragraph.getWidth());
35931 this.el.setHeight(image.getHeight());
35932 paragraph.setHeight(image.getHeight());
35938 enter: function(e, el)
35940 e.preventDefault();
35942 if(this.bgimage.length){
35943 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35944 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35948 leave: function(e, el)
35950 e.preventDefault();
35952 if(this.bgimage.length){
35953 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35954 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35969 * @class Roo.bootstrap.NumberField
35970 * @extends Roo.bootstrap.Input
35971 * Bootstrap NumberField class
35977 * Create a new NumberField
35978 * @param {Object} config The config object
35981 Roo.bootstrap.NumberField = function(config){
35982 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35985 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35988 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35990 allowDecimals : true,
35992 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35994 decimalSeparator : ".",
35996 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35998 decimalPrecision : 2,
36000 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36002 allowNegative : true,
36005 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36009 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36011 minValue : Number.NEGATIVE_INFINITY,
36013 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36015 maxValue : Number.MAX_VALUE,
36017 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36019 minText : "The minimum value for this field is {0}",
36021 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36023 maxText : "The maximum value for this field is {0}",
36025 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36026 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36028 nanText : "{0} is not a valid number",
36030 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36032 thousandsDelimiter : false,
36034 * @cfg {String} valueAlign alignment of value
36036 valueAlign : "left",
36038 getAutoCreate : function()
36040 var hiddenInput = {
36044 cls: 'hidden-number-input'
36048 hiddenInput.name = this.name;
36053 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36055 this.name = hiddenInput.name;
36057 if(cfg.cn.length > 0) {
36058 cfg.cn.push(hiddenInput);
36065 initEvents : function()
36067 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36069 var allowed = "0123456789";
36071 if(this.allowDecimals){
36072 allowed += this.decimalSeparator;
36075 if(this.allowNegative){
36079 if(this.thousandsDelimiter) {
36083 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36085 var keyPress = function(e){
36087 var k = e.getKey();
36089 var c = e.getCharCode();
36092 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36093 allowed.indexOf(String.fromCharCode(c)) === -1
36099 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36103 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36108 this.el.on("keypress", keyPress, this);
36111 validateValue : function(value)
36114 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36118 var num = this.parseValue(value);
36121 this.markInvalid(String.format(this.nanText, value));
36125 if(num < this.minValue){
36126 this.markInvalid(String.format(this.minText, this.minValue));
36130 if(num > this.maxValue){
36131 this.markInvalid(String.format(this.maxText, this.maxValue));
36138 getValue : function()
36140 var v = this.hiddenEl().getValue();
36142 return this.fixPrecision(this.parseValue(v));
36145 parseValue : function(value)
36147 if(this.thousandsDelimiter) {
36149 r = new RegExp(",", "g");
36150 value = value.replace(r, "");
36153 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36154 return isNaN(value) ? '' : value;
36157 fixPrecision : function(value)
36159 if(this.thousandsDelimiter) {
36161 r = new RegExp(",", "g");
36162 value = value.replace(r, "");
36165 var nan = isNaN(value);
36167 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36168 return nan ? '' : value;
36170 return parseFloat(value).toFixed(this.decimalPrecision);
36173 setValue : function(v)
36175 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36181 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36183 this.inputEl().dom.value = (v == '') ? '' :
36184 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36186 if(!this.allowZero && v === '0') {
36187 this.hiddenEl().dom.value = '';
36188 this.inputEl().dom.value = '';
36195 decimalPrecisionFcn : function(v)
36197 return Math.floor(v);
36200 beforeBlur : function()
36202 var v = this.parseValue(this.getRawValue());
36204 if(v || v === 0 || v === ''){
36209 hiddenEl : function()
36211 return this.el.select('input.hidden-number-input',true).first();
36223 * @class Roo.bootstrap.DocumentSlider
36224 * @extends Roo.bootstrap.Component
36225 * Bootstrap DocumentSlider class
36228 * Create a new DocumentViewer
36229 * @param {Object} config The config object
36232 Roo.bootstrap.DocumentSlider = function(config){
36233 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36240 * Fire after initEvent
36241 * @param {Roo.bootstrap.DocumentSlider} this
36246 * Fire after update
36247 * @param {Roo.bootstrap.DocumentSlider} this
36253 * @param {Roo.bootstrap.DocumentSlider} this
36259 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36265 getAutoCreate : function()
36269 cls : 'roo-document-slider',
36273 cls : 'roo-document-slider-header',
36277 cls : 'roo-document-slider-header-title'
36283 cls : 'roo-document-slider-body',
36287 cls : 'roo-document-slider-prev',
36291 cls : 'fa fa-chevron-left'
36297 cls : 'roo-document-slider-thumb',
36301 cls : 'roo-document-slider-image'
36307 cls : 'roo-document-slider-next',
36311 cls : 'fa fa-chevron-right'
36323 initEvents : function()
36325 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36326 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36328 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36329 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36331 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36332 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36334 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36335 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36337 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36338 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36340 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36341 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36343 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36344 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36346 this.thumbEl.on('click', this.onClick, this);
36348 this.prevIndicator.on('click', this.prev, this);
36350 this.nextIndicator.on('click', this.next, this);
36354 initial : function()
36356 if(this.files.length){
36357 this.indicator = 1;
36361 this.fireEvent('initial', this);
36364 update : function()
36366 this.imageEl.attr('src', this.files[this.indicator - 1]);
36368 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36370 this.prevIndicator.show();
36372 if(this.indicator == 1){
36373 this.prevIndicator.hide();
36376 this.nextIndicator.show();
36378 if(this.indicator == this.files.length){
36379 this.nextIndicator.hide();
36382 this.thumbEl.scrollTo('top');
36384 this.fireEvent('update', this);
36387 onClick : function(e)
36389 e.preventDefault();
36391 this.fireEvent('click', this);
36396 e.preventDefault();
36398 this.indicator = Math.max(1, this.indicator - 1);
36405 e.preventDefault();
36407 this.indicator = Math.min(this.files.length, this.indicator + 1);
36421 * @class Roo.bootstrap.RadioSet
36422 * @extends Roo.bootstrap.Input
36423 * Bootstrap RadioSet class
36424 * @cfg {String} indicatorpos (left|right) default left
36425 * @cfg {Boolean} inline (true|false) inline the element (default true)
36426 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36428 * Create a new RadioSet
36429 * @param {Object} config The config object
36432 Roo.bootstrap.RadioSet = function(config){
36434 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36438 Roo.bootstrap.RadioSet.register(this);
36443 * Fires when the element is checked or unchecked.
36444 * @param {Roo.bootstrap.RadioSet} this This radio
36445 * @param {Roo.bootstrap.Radio} item The checked item
36450 * Fires when the element is click.
36451 * @param {Roo.bootstrap.RadioSet} this This radio set
36452 * @param {Roo.bootstrap.Radio} item The checked item
36453 * @param {Roo.EventObject} e The event object
36460 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36468 indicatorpos : 'left',
36470 getAutoCreate : function()
36474 cls : 'roo-radio-set-label',
36478 html : this.fieldLabel
36482 if (Roo.bootstrap.version == 3) {
36485 if(this.indicatorpos == 'left'){
36488 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36489 tooltip : 'This field is required'
36494 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36495 tooltip : 'This field is required'
36501 cls : 'roo-radio-set-items'
36504 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36506 if (align === 'left' && this.fieldLabel.length) {
36509 cls : "roo-radio-set-right",
36515 if(this.labelWidth > 12){
36516 label.style = "width: " + this.labelWidth + 'px';
36519 if(this.labelWidth < 13 && this.labelmd == 0){
36520 this.labelmd = this.labelWidth;
36523 if(this.labellg > 0){
36524 label.cls += ' col-lg-' + this.labellg;
36525 items.cls += ' col-lg-' + (12 - this.labellg);
36528 if(this.labelmd > 0){
36529 label.cls += ' col-md-' + this.labelmd;
36530 items.cls += ' col-md-' + (12 - this.labelmd);
36533 if(this.labelsm > 0){
36534 label.cls += ' col-sm-' + this.labelsm;
36535 items.cls += ' col-sm-' + (12 - this.labelsm);
36538 if(this.labelxs > 0){
36539 label.cls += ' col-xs-' + this.labelxs;
36540 items.cls += ' col-xs-' + (12 - this.labelxs);
36546 cls : 'roo-radio-set',
36550 cls : 'roo-radio-set-input',
36553 value : this.value ? this.value : ''
36560 if(this.weight.length){
36561 cfg.cls += ' roo-radio-' + this.weight;
36565 cfg.cls += ' roo-radio-set-inline';
36569 ['xs','sm','md','lg'].map(function(size){
36570 if (settings[size]) {
36571 cfg.cls += ' col-' + size + '-' + settings[size];
36579 initEvents : function()
36581 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36582 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36584 if(!this.fieldLabel.length){
36585 this.labelEl.hide();
36588 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36589 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36591 this.indicator = this.indicatorEl();
36593 if(this.indicator){
36594 this.indicator.addClass('invisible');
36597 this.originalValue = this.getValue();
36601 inputEl: function ()
36603 return this.el.select('.roo-radio-set-input', true).first();
36606 getChildContainer : function()
36608 return this.itemsEl;
36611 register : function(item)
36613 this.radioes.push(item);
36617 validate : function()
36619 if(this.getVisibilityEl().hasClass('hidden')){
36625 Roo.each(this.radioes, function(i){
36634 if(this.allowBlank) {
36638 if(this.disabled || valid){
36643 this.markInvalid();
36648 markValid : function()
36650 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36651 this.indicatorEl().removeClass('visible');
36652 this.indicatorEl().addClass('invisible');
36656 if (Roo.bootstrap.version == 3) {
36657 this.el.removeClass([this.invalidClass, this.validClass]);
36658 this.el.addClass(this.validClass);
36660 this.el.removeClass(['is-invalid','is-valid']);
36661 this.el.addClass(['is-valid']);
36663 this.fireEvent('valid', this);
36666 markInvalid : function(msg)
36668 if(this.allowBlank || this.disabled){
36672 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36673 this.indicatorEl().removeClass('invisible');
36674 this.indicatorEl().addClass('visible');
36676 if (Roo.bootstrap.version == 3) {
36677 this.el.removeClass([this.invalidClass, this.validClass]);
36678 this.el.addClass(this.invalidClass);
36680 this.el.removeClass(['is-invalid','is-valid']);
36681 this.el.addClass(['is-invalid']);
36684 this.fireEvent('invalid', this, msg);
36688 setValue : function(v, suppressEvent)
36690 if(this.value === v){
36697 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36700 Roo.each(this.radioes, function(i){
36702 i.el.removeClass('checked');
36705 Roo.each(this.radioes, function(i){
36707 if(i.value === v || i.value.toString() === v.toString()){
36709 i.el.addClass('checked');
36711 if(suppressEvent !== true){
36712 this.fireEvent('check', this, i);
36723 clearInvalid : function(){
36725 if(!this.el || this.preventMark){
36729 this.el.removeClass([this.invalidClass]);
36731 this.fireEvent('valid', this);
36736 Roo.apply(Roo.bootstrap.RadioSet, {
36740 register : function(set)
36742 this.groups[set.name] = set;
36745 get: function(name)
36747 if (typeof(this.groups[name]) == 'undefined') {
36751 return this.groups[name] ;
36757 * Ext JS Library 1.1.1
36758 * Copyright(c) 2006-2007, Ext JS, LLC.
36760 * Originally Released Under LGPL - original licence link has changed is not relivant.
36763 * <script type="text/javascript">
36768 * @class Roo.bootstrap.SplitBar
36769 * @extends Roo.util.Observable
36770 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36774 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36775 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36776 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36777 split.minSize = 100;
36778 split.maxSize = 600;
36779 split.animate = true;
36780 split.on('moved', splitterMoved);
36783 * Create a new SplitBar
36784 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36785 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36786 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36787 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36788 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36789 position of the SplitBar).
36791 Roo.bootstrap.SplitBar = function(cfg){
36796 // dragElement : elm
36797 // resizingElement: el,
36799 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36800 // placement : Roo.bootstrap.SplitBar.LEFT ,
36801 // existingProxy ???
36804 this.el = Roo.get(cfg.dragElement, true);
36805 this.el.dom.unselectable = "on";
36807 this.resizingEl = Roo.get(cfg.resizingElement, true);
36811 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36812 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36815 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36818 * The minimum size of the resizing element. (Defaults to 0)
36824 * The maximum size of the resizing element. (Defaults to 2000)
36827 this.maxSize = 2000;
36830 * Whether to animate the transition to the new size
36833 this.animate = false;
36836 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36839 this.useShim = false;
36844 if(!cfg.existingProxy){
36846 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36848 this.proxy = Roo.get(cfg.existingProxy).dom;
36851 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36854 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36857 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36860 this.dragSpecs = {};
36863 * @private The adapter to use to positon and resize elements
36865 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36866 this.adapter.init(this);
36868 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36870 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36871 this.el.addClass("roo-splitbar-h");
36874 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36875 this.el.addClass("roo-splitbar-v");
36881 * Fires when the splitter is moved (alias for {@link #event-moved})
36882 * @param {Roo.bootstrap.SplitBar} this
36883 * @param {Number} newSize the new width or height
36888 * Fires when the splitter is moved
36889 * @param {Roo.bootstrap.SplitBar} this
36890 * @param {Number} newSize the new width or height
36894 * @event beforeresize
36895 * Fires before the splitter is dragged
36896 * @param {Roo.bootstrap.SplitBar} this
36898 "beforeresize" : true,
36900 "beforeapply" : true
36903 Roo.util.Observable.call(this);
36906 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36907 onStartProxyDrag : function(x, y){
36908 this.fireEvent("beforeresize", this);
36910 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36912 o.enableDisplayMode("block");
36913 // all splitbars share the same overlay
36914 Roo.bootstrap.SplitBar.prototype.overlay = o;
36916 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36917 this.overlay.show();
36918 Roo.get(this.proxy).setDisplayed("block");
36919 var size = this.adapter.getElementSize(this);
36920 this.activeMinSize = this.getMinimumSize();;
36921 this.activeMaxSize = this.getMaximumSize();;
36922 var c1 = size - this.activeMinSize;
36923 var c2 = Math.max(this.activeMaxSize - size, 0);
36924 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36925 this.dd.resetConstraints();
36926 this.dd.setXConstraint(
36927 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36928 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36930 this.dd.setYConstraint(0, 0);
36932 this.dd.resetConstraints();
36933 this.dd.setXConstraint(0, 0);
36934 this.dd.setYConstraint(
36935 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36936 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36939 this.dragSpecs.startSize = size;
36940 this.dragSpecs.startPoint = [x, y];
36941 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36945 * @private Called after the drag operation by the DDProxy
36947 onEndProxyDrag : function(e){
36948 Roo.get(this.proxy).setDisplayed(false);
36949 var endPoint = Roo.lib.Event.getXY(e);
36951 this.overlay.hide();
36954 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36955 newSize = this.dragSpecs.startSize +
36956 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36957 endPoint[0] - this.dragSpecs.startPoint[0] :
36958 this.dragSpecs.startPoint[0] - endPoint[0]
36961 newSize = this.dragSpecs.startSize +
36962 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36963 endPoint[1] - this.dragSpecs.startPoint[1] :
36964 this.dragSpecs.startPoint[1] - endPoint[1]
36967 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36968 if(newSize != this.dragSpecs.startSize){
36969 if(this.fireEvent('beforeapply', this, newSize) !== false){
36970 this.adapter.setElementSize(this, newSize);
36971 this.fireEvent("moved", this, newSize);
36972 this.fireEvent("resize", this, newSize);
36978 * Get the adapter this SplitBar uses
36979 * @return The adapter object
36981 getAdapter : function(){
36982 return this.adapter;
36986 * Set the adapter this SplitBar uses
36987 * @param {Object} adapter A SplitBar adapter object
36989 setAdapter : function(adapter){
36990 this.adapter = adapter;
36991 this.adapter.init(this);
36995 * Gets the minimum size for the resizing element
36996 * @return {Number} The minimum size
36998 getMinimumSize : function(){
36999 return this.minSize;
37003 * Sets the minimum size for the resizing element
37004 * @param {Number} minSize The minimum size
37006 setMinimumSize : function(minSize){
37007 this.minSize = minSize;
37011 * Gets the maximum size for the resizing element
37012 * @return {Number} The maximum size
37014 getMaximumSize : function(){
37015 return this.maxSize;
37019 * Sets the maximum size for the resizing element
37020 * @param {Number} maxSize The maximum size
37022 setMaximumSize : function(maxSize){
37023 this.maxSize = maxSize;
37027 * Sets the initialize size for the resizing element
37028 * @param {Number} size The initial size
37030 setCurrentSize : function(size){
37031 var oldAnimate = this.animate;
37032 this.animate = false;
37033 this.adapter.setElementSize(this, size);
37034 this.animate = oldAnimate;
37038 * Destroy this splitbar.
37039 * @param {Boolean} removeEl True to remove the element
37041 destroy : function(removeEl){
37043 this.shim.remove();
37046 this.proxy.parentNode.removeChild(this.proxy);
37054 * @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.
37056 Roo.bootstrap.SplitBar.createProxy = function(dir){
37057 var proxy = new Roo.Element(document.createElement("div"));
37058 proxy.unselectable();
37059 var cls = 'roo-splitbar-proxy';
37060 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37061 document.body.appendChild(proxy.dom);
37066 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37067 * Default Adapter. It assumes the splitter and resizing element are not positioned
37068 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37070 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37073 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37074 // do nothing for now
37075 init : function(s){
37079 * Called before drag operations to get the current size of the resizing element.
37080 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37082 getElementSize : function(s){
37083 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37084 return s.resizingEl.getWidth();
37086 return s.resizingEl.getHeight();
37091 * Called after drag operations to set the size of the resizing element.
37092 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37093 * @param {Number} newSize The new size to set
37094 * @param {Function} onComplete A function to be invoked when resizing is complete
37096 setElementSize : function(s, newSize, onComplete){
37097 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37099 s.resizingEl.setWidth(newSize);
37101 onComplete(s, newSize);
37104 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37109 s.resizingEl.setHeight(newSize);
37111 onComplete(s, newSize);
37114 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37121 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37122 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37123 * Adapter that moves the splitter element to align with the resized sizing element.
37124 * Used with an absolute positioned SplitBar.
37125 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37126 * document.body, make sure you assign an id to the body element.
37128 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37129 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37130 this.container = Roo.get(container);
37133 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37134 init : function(s){
37135 this.basic.init(s);
37138 getElementSize : function(s){
37139 return this.basic.getElementSize(s);
37142 setElementSize : function(s, newSize, onComplete){
37143 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37146 moveSplitter : function(s){
37147 var yes = Roo.bootstrap.SplitBar;
37148 switch(s.placement){
37150 s.el.setX(s.resizingEl.getRight());
37153 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37156 s.el.setY(s.resizingEl.getBottom());
37159 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37166 * Orientation constant - Create a vertical SplitBar
37170 Roo.bootstrap.SplitBar.VERTICAL = 1;
37173 * Orientation constant - Create a horizontal SplitBar
37177 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37180 * Placement constant - The resizing element is to the left of the splitter element
37184 Roo.bootstrap.SplitBar.LEFT = 1;
37187 * Placement constant - The resizing element is to the right of the splitter element
37191 Roo.bootstrap.SplitBar.RIGHT = 2;
37194 * Placement constant - The resizing element is positioned above the splitter element
37198 Roo.bootstrap.SplitBar.TOP = 3;
37201 * Placement constant - The resizing element is positioned under splitter element
37205 Roo.bootstrap.SplitBar.BOTTOM = 4;
37206 Roo.namespace("Roo.bootstrap.layout");/*
37208 * Ext JS Library 1.1.1
37209 * Copyright(c) 2006-2007, Ext JS, LLC.
37211 * Originally Released Under LGPL - original licence link has changed is not relivant.
37214 * <script type="text/javascript">
37218 * @class Roo.bootstrap.layout.Manager
37219 * @extends Roo.bootstrap.Component
37220 * Base class for layout managers.
37222 Roo.bootstrap.layout.Manager = function(config)
37224 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37230 /** false to disable window resize monitoring @type Boolean */
37231 this.monitorWindowResize = true;
37236 * Fires when a layout is performed.
37237 * @param {Roo.LayoutManager} this
37241 * @event regionresized
37242 * Fires when the user resizes a region.
37243 * @param {Roo.LayoutRegion} region The resized region
37244 * @param {Number} newSize The new size (width for east/west, height for north/south)
37246 "regionresized" : true,
37248 * @event regioncollapsed
37249 * Fires when a region is collapsed.
37250 * @param {Roo.LayoutRegion} region The collapsed region
37252 "regioncollapsed" : true,
37254 * @event regionexpanded
37255 * Fires when a region is expanded.
37256 * @param {Roo.LayoutRegion} region The expanded region
37258 "regionexpanded" : true
37260 this.updating = false;
37263 this.el = Roo.get(config.el);
37269 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37274 monitorWindowResize : true,
37280 onRender : function(ct, position)
37283 this.el = Roo.get(ct);
37286 //this.fireEvent('render',this);
37290 initEvents: function()
37294 // ie scrollbar fix
37295 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37296 document.body.scroll = "no";
37297 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37298 this.el.position('relative');
37300 this.id = this.el.id;
37301 this.el.addClass("roo-layout-container");
37302 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37303 if(this.el.dom != document.body ) {
37304 this.el.on('resize', this.layout,this);
37305 this.el.on('show', this.layout,this);
37311 * Returns true if this layout is currently being updated
37312 * @return {Boolean}
37314 isUpdating : function(){
37315 return this.updating;
37319 * Suspend the LayoutManager from doing auto-layouts while
37320 * making multiple add or remove calls
37322 beginUpdate : function(){
37323 this.updating = true;
37327 * Restore auto-layouts and optionally disable the manager from performing a layout
37328 * @param {Boolean} noLayout true to disable a layout update
37330 endUpdate : function(noLayout){
37331 this.updating = false;
37337 layout: function(){
37341 onRegionResized : function(region, newSize){
37342 this.fireEvent("regionresized", region, newSize);
37346 onRegionCollapsed : function(region){
37347 this.fireEvent("regioncollapsed", region);
37350 onRegionExpanded : function(region){
37351 this.fireEvent("regionexpanded", region);
37355 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37356 * performs box-model adjustments.
37357 * @return {Object} The size as an object {width: (the width), height: (the height)}
37359 getViewSize : function()
37362 if(this.el.dom != document.body){
37363 size = this.el.getSize();
37365 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37367 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37368 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37373 * Returns the Element this layout is bound to.
37374 * @return {Roo.Element}
37376 getEl : function(){
37381 * Returns the specified region.
37382 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37383 * @return {Roo.LayoutRegion}
37385 getRegion : function(target){
37386 return this.regions[target.toLowerCase()];
37389 onWindowResize : function(){
37390 if(this.monitorWindowResize){
37397 * Ext JS Library 1.1.1
37398 * Copyright(c) 2006-2007, Ext JS, LLC.
37400 * Originally Released Under LGPL - original licence link has changed is not relivant.
37403 * <script type="text/javascript">
37406 * @class Roo.bootstrap.layout.Border
37407 * @extends Roo.bootstrap.layout.Manager
37408 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37409 * please see: examples/bootstrap/nested.html<br><br>
37411 <b>The container the layout is rendered into can be either the body element or any other element.
37412 If it is not the body element, the container needs to either be an absolute positioned element,
37413 or you will need to add "position:relative" to the css of the container. You will also need to specify
37414 the container size if it is not the body element.</b>
37417 * Create a new Border
37418 * @param {Object} config Configuration options
37420 Roo.bootstrap.layout.Border = function(config){
37421 config = config || {};
37422 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37426 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37427 if(config[region]){
37428 config[region].region = region;
37429 this.addRegion(config[region]);
37435 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37437 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37439 parent : false, // this might point to a 'nest' or a ???
37442 * Creates and adds a new region if it doesn't already exist.
37443 * @param {String} target The target region key (north, south, east, west or center).
37444 * @param {Object} config The regions config object
37445 * @return {BorderLayoutRegion} The new region
37447 addRegion : function(config)
37449 if(!this.regions[config.region]){
37450 var r = this.factory(config);
37451 this.bindRegion(r);
37453 return this.regions[config.region];
37457 bindRegion : function(r){
37458 this.regions[r.config.region] = r;
37460 r.on("visibilitychange", this.layout, this);
37461 r.on("paneladded", this.layout, this);
37462 r.on("panelremoved", this.layout, this);
37463 r.on("invalidated", this.layout, this);
37464 r.on("resized", this.onRegionResized, this);
37465 r.on("collapsed", this.onRegionCollapsed, this);
37466 r.on("expanded", this.onRegionExpanded, this);
37470 * Performs a layout update.
37472 layout : function()
37474 if(this.updating) {
37478 // render all the rebions if they have not been done alreayd?
37479 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37480 if(this.regions[region] && !this.regions[region].bodyEl){
37481 this.regions[region].onRender(this.el)
37485 var size = this.getViewSize();
37486 var w = size.width;
37487 var h = size.height;
37492 //var x = 0, y = 0;
37494 var rs = this.regions;
37495 var north = rs["north"];
37496 var south = rs["south"];
37497 var west = rs["west"];
37498 var east = rs["east"];
37499 var center = rs["center"];
37500 //if(this.hideOnLayout){ // not supported anymore
37501 //c.el.setStyle("display", "none");
37503 if(north && north.isVisible()){
37504 var b = north.getBox();
37505 var m = north.getMargins();
37506 b.width = w - (m.left+m.right);
37509 centerY = b.height + b.y + m.bottom;
37510 centerH -= centerY;
37511 north.updateBox(this.safeBox(b));
37513 if(south && south.isVisible()){
37514 var b = south.getBox();
37515 var m = south.getMargins();
37516 b.width = w - (m.left+m.right);
37518 var totalHeight = (b.height + m.top + m.bottom);
37519 b.y = h - totalHeight + m.top;
37520 centerH -= totalHeight;
37521 south.updateBox(this.safeBox(b));
37523 if(west && west.isVisible()){
37524 var b = west.getBox();
37525 var m = west.getMargins();
37526 b.height = centerH - (m.top+m.bottom);
37528 b.y = centerY + m.top;
37529 var totalWidth = (b.width + m.left + m.right);
37530 centerX += totalWidth;
37531 centerW -= totalWidth;
37532 west.updateBox(this.safeBox(b));
37534 if(east && east.isVisible()){
37535 var b = east.getBox();
37536 var m = east.getMargins();
37537 b.height = centerH - (m.top+m.bottom);
37538 var totalWidth = (b.width + m.left + m.right);
37539 b.x = w - totalWidth + m.left;
37540 b.y = centerY + m.top;
37541 centerW -= totalWidth;
37542 east.updateBox(this.safeBox(b));
37545 var m = center.getMargins();
37547 x: centerX + m.left,
37548 y: centerY + m.top,
37549 width: centerW - (m.left+m.right),
37550 height: centerH - (m.top+m.bottom)
37552 //if(this.hideOnLayout){
37553 //center.el.setStyle("display", "block");
37555 center.updateBox(this.safeBox(centerBox));
37558 this.fireEvent("layout", this);
37562 safeBox : function(box){
37563 box.width = Math.max(0, box.width);
37564 box.height = Math.max(0, box.height);
37569 * Adds a ContentPanel (or subclass) to this layout.
37570 * @param {String} target The target region key (north, south, east, west or center).
37571 * @param {Roo.ContentPanel} panel The panel to add
37572 * @return {Roo.ContentPanel} The added panel
37574 add : function(target, panel){
37576 target = target.toLowerCase();
37577 return this.regions[target].add(panel);
37581 * Remove a ContentPanel (or subclass) to this layout.
37582 * @param {String} target The target region key (north, south, east, west or center).
37583 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37584 * @return {Roo.ContentPanel} The removed panel
37586 remove : function(target, panel){
37587 target = target.toLowerCase();
37588 return this.regions[target].remove(panel);
37592 * Searches all regions for a panel with the specified id
37593 * @param {String} panelId
37594 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37596 findPanel : function(panelId){
37597 var rs = this.regions;
37598 for(var target in rs){
37599 if(typeof rs[target] != "function"){
37600 var p = rs[target].getPanel(panelId);
37610 * Searches all regions for a panel with the specified id and activates (shows) it.
37611 * @param {String/ContentPanel} panelId The panels id or the panel itself
37612 * @return {Roo.ContentPanel} The shown panel or null
37614 showPanel : function(panelId) {
37615 var rs = this.regions;
37616 for(var target in rs){
37617 var r = rs[target];
37618 if(typeof r != "function"){
37619 if(r.hasPanel(panelId)){
37620 return r.showPanel(panelId);
37628 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37629 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37632 restoreState : function(provider){
37634 provider = Roo.state.Manager;
37636 var sm = new Roo.LayoutStateManager();
37637 sm.init(this, provider);
37643 * Adds a xtype elements to the layout.
37647 xtype : 'ContentPanel',
37654 xtype : 'NestedLayoutPanel',
37660 items : [ ... list of content panels or nested layout panels.. ]
37664 * @param {Object} cfg Xtype definition of item to add.
37666 addxtype : function(cfg)
37668 // basically accepts a pannel...
37669 // can accept a layout region..!?!?
37670 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37673 // theory? children can only be panels??
37675 //if (!cfg.xtype.match(/Panel$/)) {
37680 if (typeof(cfg.region) == 'undefined') {
37681 Roo.log("Failed to add Panel, region was not set");
37685 var region = cfg.region;
37691 xitems = cfg.items;
37696 if ( region == 'center') {
37697 Roo.log("Center: " + cfg.title);
37703 case 'Content': // ContentPanel (el, cfg)
37704 case 'Scroll': // ContentPanel (el, cfg)
37706 cfg.autoCreate = cfg.autoCreate || true;
37707 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37709 // var el = this.el.createChild();
37710 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37713 this.add(region, ret);
37717 case 'TreePanel': // our new panel!
37718 cfg.el = this.el.createChild();
37719 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37720 this.add(region, ret);
37725 // create a new Layout (which is a Border Layout...
37727 var clayout = cfg.layout;
37728 clayout.el = this.el.createChild();
37729 clayout.items = clayout.items || [];
37733 // replace this exitems with the clayout ones..
37734 xitems = clayout.items;
37736 // force background off if it's in center...
37737 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37738 cfg.background = false;
37740 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37743 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37744 //console.log('adding nested layout panel ' + cfg.toSource());
37745 this.add(region, ret);
37746 nb = {}; /// find first...
37751 // needs grid and region
37753 //var el = this.getRegion(region).el.createChild();
37755 *var el = this.el.createChild();
37756 // create the grid first...
37757 cfg.grid.container = el;
37758 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37761 if (region == 'center' && this.active ) {
37762 cfg.background = false;
37765 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37767 this.add(region, ret);
37769 if (cfg.background) {
37770 // render grid on panel activation (if panel background)
37771 ret.on('activate', function(gp) {
37772 if (!gp.grid.rendered) {
37773 // gp.grid.render(el);
37777 // cfg.grid.render(el);
37783 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37784 // it was the old xcomponent building that caused this before.
37785 // espeically if border is the top element in the tree.
37795 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37797 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37798 this.add(region, ret);
37802 throw "Can not add '" + cfg.xtype + "' to Border";
37808 this.beginUpdate();
37812 Roo.each(xitems, function(i) {
37813 region = nb && i.region ? i.region : false;
37815 var add = ret.addxtype(i);
37818 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37819 if (!i.background) {
37820 abn[region] = nb[region] ;
37827 // make the last non-background panel active..
37828 //if (nb) { Roo.log(abn); }
37831 for(var r in abn) {
37832 region = this.getRegion(r);
37834 // tried using nb[r], but it does not work..
37836 region.showPanel(abn[r]);
37847 factory : function(cfg)
37850 var validRegions = Roo.bootstrap.layout.Border.regions;
37852 var target = cfg.region;
37855 var r = Roo.bootstrap.layout;
37859 return new r.North(cfg);
37861 return new r.South(cfg);
37863 return new r.East(cfg);
37865 return new r.West(cfg);
37867 return new r.Center(cfg);
37869 throw 'Layout region "'+target+'" not supported.';
37876 * Ext JS Library 1.1.1
37877 * Copyright(c) 2006-2007, Ext JS, LLC.
37879 * Originally Released Under LGPL - original licence link has changed is not relivant.
37882 * <script type="text/javascript">
37886 * @class Roo.bootstrap.layout.Basic
37887 * @extends Roo.util.Observable
37888 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37889 * and does not have a titlebar, tabs or any other features. All it does is size and position
37890 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37891 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37892 * @cfg {string} region the region that it inhabits..
37893 * @cfg {bool} skipConfig skip config?
37897 Roo.bootstrap.layout.Basic = function(config){
37899 this.mgr = config.mgr;
37901 this.position = config.region;
37903 var skipConfig = config.skipConfig;
37907 * @scope Roo.BasicLayoutRegion
37911 * @event beforeremove
37912 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37913 * @param {Roo.LayoutRegion} this
37914 * @param {Roo.ContentPanel} panel The panel
37915 * @param {Object} e The cancel event object
37917 "beforeremove" : true,
37919 * @event invalidated
37920 * Fires when the layout for this region is changed.
37921 * @param {Roo.LayoutRegion} this
37923 "invalidated" : true,
37925 * @event visibilitychange
37926 * Fires when this region is shown or hidden
37927 * @param {Roo.LayoutRegion} this
37928 * @param {Boolean} visibility true or false
37930 "visibilitychange" : true,
37932 * @event paneladded
37933 * Fires when a panel is added.
37934 * @param {Roo.LayoutRegion} this
37935 * @param {Roo.ContentPanel} panel The panel
37937 "paneladded" : true,
37939 * @event panelremoved
37940 * Fires when a panel is removed.
37941 * @param {Roo.LayoutRegion} this
37942 * @param {Roo.ContentPanel} panel The panel
37944 "panelremoved" : true,
37946 * @event beforecollapse
37947 * Fires when this region before collapse.
37948 * @param {Roo.LayoutRegion} this
37950 "beforecollapse" : true,
37953 * Fires when this region is collapsed.
37954 * @param {Roo.LayoutRegion} this
37956 "collapsed" : true,
37959 * Fires when this region is expanded.
37960 * @param {Roo.LayoutRegion} this
37965 * Fires when this region is slid into view.
37966 * @param {Roo.LayoutRegion} this
37968 "slideshow" : true,
37971 * Fires when this region slides out of view.
37972 * @param {Roo.LayoutRegion} this
37974 "slidehide" : true,
37976 * @event panelactivated
37977 * Fires when a panel is activated.
37978 * @param {Roo.LayoutRegion} this
37979 * @param {Roo.ContentPanel} panel The activated panel
37981 "panelactivated" : true,
37984 * Fires when the user resizes this region.
37985 * @param {Roo.LayoutRegion} this
37986 * @param {Number} newSize The new size (width for east/west, height for north/south)
37990 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37991 this.panels = new Roo.util.MixedCollection();
37992 this.panels.getKey = this.getPanelId.createDelegate(this);
37994 this.activePanel = null;
37995 // ensure listeners are added...
37997 if (config.listeners || config.events) {
37998 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37999 listeners : config.listeners || {},
38000 events : config.events || {}
38004 if(skipConfig !== true){
38005 this.applyConfig(config);
38009 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38011 getPanelId : function(p){
38015 applyConfig : function(config){
38016 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38017 this.config = config;
38022 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38023 * the width, for horizontal (north, south) the height.
38024 * @param {Number} newSize The new width or height
38026 resizeTo : function(newSize){
38027 var el = this.el ? this.el :
38028 (this.activePanel ? this.activePanel.getEl() : null);
38030 switch(this.position){
38033 el.setWidth(newSize);
38034 this.fireEvent("resized", this, newSize);
38038 el.setHeight(newSize);
38039 this.fireEvent("resized", this, newSize);
38045 getBox : function(){
38046 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38049 getMargins : function(){
38050 return this.margins;
38053 updateBox : function(box){
38055 var el = this.activePanel.getEl();
38056 el.dom.style.left = box.x + "px";
38057 el.dom.style.top = box.y + "px";
38058 this.activePanel.setSize(box.width, box.height);
38062 * Returns the container element for this region.
38063 * @return {Roo.Element}
38065 getEl : function(){
38066 return this.activePanel;
38070 * Returns true if this region is currently visible.
38071 * @return {Boolean}
38073 isVisible : function(){
38074 return this.activePanel ? true : false;
38077 setActivePanel : function(panel){
38078 panel = this.getPanel(panel);
38079 if(this.activePanel && this.activePanel != panel){
38080 this.activePanel.setActiveState(false);
38081 this.activePanel.getEl().setLeftTop(-10000,-10000);
38083 this.activePanel = panel;
38084 panel.setActiveState(true);
38086 panel.setSize(this.box.width, this.box.height);
38088 this.fireEvent("panelactivated", this, panel);
38089 this.fireEvent("invalidated");
38093 * Show the specified panel.
38094 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38095 * @return {Roo.ContentPanel} The shown panel or null
38097 showPanel : function(panel){
38098 panel = this.getPanel(panel);
38100 this.setActivePanel(panel);
38106 * Get the active panel for this region.
38107 * @return {Roo.ContentPanel} The active panel or null
38109 getActivePanel : function(){
38110 return this.activePanel;
38114 * Add the passed ContentPanel(s)
38115 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38116 * @return {Roo.ContentPanel} The panel added (if only one was added)
38118 add : function(panel){
38119 if(arguments.length > 1){
38120 for(var i = 0, len = arguments.length; i < len; i++) {
38121 this.add(arguments[i]);
38125 if(this.hasPanel(panel)){
38126 this.showPanel(panel);
38129 var el = panel.getEl();
38130 if(el.dom.parentNode != this.mgr.el.dom){
38131 this.mgr.el.dom.appendChild(el.dom);
38133 if(panel.setRegion){
38134 panel.setRegion(this);
38136 this.panels.add(panel);
38137 el.setStyle("position", "absolute");
38138 if(!panel.background){
38139 this.setActivePanel(panel);
38140 if(this.config.initialSize && this.panels.getCount()==1){
38141 this.resizeTo(this.config.initialSize);
38144 this.fireEvent("paneladded", this, panel);
38149 * Returns true if the panel is in this region.
38150 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38151 * @return {Boolean}
38153 hasPanel : function(panel){
38154 if(typeof panel == "object"){ // must be panel obj
38155 panel = panel.getId();
38157 return this.getPanel(panel) ? true : false;
38161 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38162 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38163 * @param {Boolean} preservePanel Overrides the config preservePanel option
38164 * @return {Roo.ContentPanel} The panel that was removed
38166 remove : function(panel, preservePanel){
38167 panel = this.getPanel(panel);
38172 this.fireEvent("beforeremove", this, panel, e);
38173 if(e.cancel === true){
38176 var panelId = panel.getId();
38177 this.panels.removeKey(panelId);
38182 * Returns the panel specified or null if it's not in this region.
38183 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38184 * @return {Roo.ContentPanel}
38186 getPanel : function(id){
38187 if(typeof id == "object"){ // must be panel obj
38190 return this.panels.get(id);
38194 * Returns this regions position (north/south/east/west/center).
38197 getPosition: function(){
38198 return this.position;
38202 * Ext JS Library 1.1.1
38203 * Copyright(c) 2006-2007, Ext JS, LLC.
38205 * Originally Released Under LGPL - original licence link has changed is not relivant.
38208 * <script type="text/javascript">
38212 * @class Roo.bootstrap.layout.Region
38213 * @extends Roo.bootstrap.layout.Basic
38214 * This class represents a region in a layout manager.
38216 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38217 * @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})
38218 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38219 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38220 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38221 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38222 * @cfg {String} title The title for the region (overrides panel titles)
38223 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38224 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38225 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38226 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38227 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38228 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38229 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38230 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38231 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38232 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38234 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38235 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38236 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38237 * @cfg {Number} width For East/West panels
38238 * @cfg {Number} height For North/South panels
38239 * @cfg {Boolean} split To show the splitter
38240 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38242 * @cfg {string} cls Extra CSS classes to add to region
38244 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38245 * @cfg {string} region the region that it inhabits..
38248 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38249 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38251 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38252 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38253 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38255 Roo.bootstrap.layout.Region = function(config)
38257 this.applyConfig(config);
38259 var mgr = config.mgr;
38260 var pos = config.region;
38261 config.skipConfig = true;
38262 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38265 this.onRender(mgr.el);
38268 this.visible = true;
38269 this.collapsed = false;
38270 this.unrendered_panels = [];
38273 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38275 position: '', // set by wrapper (eg. north/south etc..)
38276 unrendered_panels : null, // unrendered panels.
38278 tabPosition : false,
38280 mgr: false, // points to 'Border'
38283 createBody : function(){
38284 /** This region's body element
38285 * @type Roo.Element */
38286 this.bodyEl = this.el.createChild({
38288 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38292 onRender: function(ctr, pos)
38294 var dh = Roo.DomHelper;
38295 /** This region's container element
38296 * @type Roo.Element */
38297 this.el = dh.append(ctr.dom, {
38299 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38301 /** This region's title element
38302 * @type Roo.Element */
38304 this.titleEl = dh.append(this.el.dom, {
38306 unselectable: "on",
38307 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38309 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38310 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38314 this.titleEl.enableDisplayMode();
38315 /** This region's title text element
38316 * @type HTMLElement */
38317 this.titleTextEl = this.titleEl.dom.firstChild;
38318 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38320 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38321 this.closeBtn.enableDisplayMode();
38322 this.closeBtn.on("click", this.closeClicked, this);
38323 this.closeBtn.hide();
38325 this.createBody(this.config);
38326 if(this.config.hideWhenEmpty){
38328 this.on("paneladded", this.validateVisibility, this);
38329 this.on("panelremoved", this.validateVisibility, this);
38331 if(this.autoScroll){
38332 this.bodyEl.setStyle("overflow", "auto");
38334 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38336 //if(c.titlebar !== false){
38337 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38338 this.titleEl.hide();
38340 this.titleEl.show();
38341 if(this.config.title){
38342 this.titleTextEl.innerHTML = this.config.title;
38346 if(this.config.collapsed){
38347 this.collapse(true);
38349 if(this.config.hidden){
38353 if (this.unrendered_panels && this.unrendered_panels.length) {
38354 for (var i =0;i< this.unrendered_panels.length; i++) {
38355 this.add(this.unrendered_panels[i]);
38357 this.unrendered_panels = null;
38363 applyConfig : function(c)
38366 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38367 var dh = Roo.DomHelper;
38368 if(c.titlebar !== false){
38369 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38370 this.collapseBtn.on("click", this.collapse, this);
38371 this.collapseBtn.enableDisplayMode();
38373 if(c.showPin === true || this.showPin){
38374 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38375 this.stickBtn.enableDisplayMode();
38376 this.stickBtn.on("click", this.expand, this);
38377 this.stickBtn.hide();
38382 /** This region's collapsed element
38383 * @type Roo.Element */
38386 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38387 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38390 if(c.floatable !== false){
38391 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38392 this.collapsedEl.on("click", this.collapseClick, this);
38395 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38396 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38397 id: "message", unselectable: "on", style:{"float":"left"}});
38398 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38400 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38401 this.expandBtn.on("click", this.expand, this);
38405 if(this.collapseBtn){
38406 this.collapseBtn.setVisible(c.collapsible == true);
38409 this.cmargins = c.cmargins || this.cmargins ||
38410 (this.position == "west" || this.position == "east" ?
38411 {top: 0, left: 2, right:2, bottom: 0} :
38412 {top: 2, left: 0, right:0, bottom: 2});
38414 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38417 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38419 this.autoScroll = c.autoScroll || false;
38424 this.duration = c.duration || .30;
38425 this.slideDuration = c.slideDuration || .45;
38430 * Returns true if this region is currently visible.
38431 * @return {Boolean}
38433 isVisible : function(){
38434 return this.visible;
38438 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38439 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38441 //setCollapsedTitle : function(title){
38442 // title = title || " ";
38443 // if(this.collapsedTitleTextEl){
38444 // this.collapsedTitleTextEl.innerHTML = title;
38448 getBox : function(){
38450 // if(!this.collapsed){
38451 b = this.el.getBox(false, true);
38453 // b = this.collapsedEl.getBox(false, true);
38458 getMargins : function(){
38459 return this.margins;
38460 //return this.collapsed ? this.cmargins : this.margins;
38463 highlight : function(){
38464 this.el.addClass("x-layout-panel-dragover");
38467 unhighlight : function(){
38468 this.el.removeClass("x-layout-panel-dragover");
38471 updateBox : function(box)
38473 if (!this.bodyEl) {
38474 return; // not rendered yet..
38478 if(!this.collapsed){
38479 this.el.dom.style.left = box.x + "px";
38480 this.el.dom.style.top = box.y + "px";
38481 this.updateBody(box.width, box.height);
38483 this.collapsedEl.dom.style.left = box.x + "px";
38484 this.collapsedEl.dom.style.top = box.y + "px";
38485 this.collapsedEl.setSize(box.width, box.height);
38488 this.tabs.autoSizeTabs();
38492 updateBody : function(w, h)
38495 this.el.setWidth(w);
38496 w -= this.el.getBorderWidth("rl");
38497 if(this.config.adjustments){
38498 w += this.config.adjustments[0];
38501 if(h !== null && h > 0){
38502 this.el.setHeight(h);
38503 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38504 h -= this.el.getBorderWidth("tb");
38505 if(this.config.adjustments){
38506 h += this.config.adjustments[1];
38508 this.bodyEl.setHeight(h);
38510 h = this.tabs.syncHeight(h);
38513 if(this.panelSize){
38514 w = w !== null ? w : this.panelSize.width;
38515 h = h !== null ? h : this.panelSize.height;
38517 if(this.activePanel){
38518 var el = this.activePanel.getEl();
38519 w = w !== null ? w : el.getWidth();
38520 h = h !== null ? h : el.getHeight();
38521 this.panelSize = {width: w, height: h};
38522 this.activePanel.setSize(w, h);
38524 if(Roo.isIE && this.tabs){
38525 this.tabs.el.repaint();
38530 * Returns the container element for this region.
38531 * @return {Roo.Element}
38533 getEl : function(){
38538 * Hides this region.
38541 //if(!this.collapsed){
38542 this.el.dom.style.left = "-2000px";
38545 // this.collapsedEl.dom.style.left = "-2000px";
38546 // this.collapsedEl.hide();
38548 this.visible = false;
38549 this.fireEvent("visibilitychange", this, false);
38553 * Shows this region if it was previously hidden.
38556 //if(!this.collapsed){
38559 // this.collapsedEl.show();
38561 this.visible = true;
38562 this.fireEvent("visibilitychange", this, true);
38565 closeClicked : function(){
38566 if(this.activePanel){
38567 this.remove(this.activePanel);
38571 collapseClick : function(e){
38573 e.stopPropagation();
38576 e.stopPropagation();
38582 * Collapses this region.
38583 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38586 collapse : function(skipAnim, skipCheck = false){
38587 if(this.collapsed) {
38591 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38593 this.collapsed = true;
38595 this.split.el.hide();
38597 if(this.config.animate && skipAnim !== true){
38598 this.fireEvent("invalidated", this);
38599 this.animateCollapse();
38601 this.el.setLocation(-20000,-20000);
38603 this.collapsedEl.show();
38604 this.fireEvent("collapsed", this);
38605 this.fireEvent("invalidated", this);
38611 animateCollapse : function(){
38616 * Expands this region if it was previously collapsed.
38617 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38618 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38621 expand : function(e, skipAnim){
38623 e.stopPropagation();
38625 if(!this.collapsed || this.el.hasActiveFx()) {
38629 this.afterSlideIn();
38632 this.collapsed = false;
38633 if(this.config.animate && skipAnim !== true){
38634 this.animateExpand();
38638 this.split.el.show();
38640 this.collapsedEl.setLocation(-2000,-2000);
38641 this.collapsedEl.hide();
38642 this.fireEvent("invalidated", this);
38643 this.fireEvent("expanded", this);
38647 animateExpand : function(){
38651 initTabs : function()
38653 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38655 var ts = new Roo.bootstrap.panel.Tabs({
38656 el: this.bodyEl.dom,
38658 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38659 disableTooltips: this.config.disableTabTips,
38660 toolbar : this.config.toolbar
38663 if(this.config.hideTabs){
38664 ts.stripWrap.setDisplayed(false);
38667 ts.resizeTabs = this.config.resizeTabs === true;
38668 ts.minTabWidth = this.config.minTabWidth || 40;
38669 ts.maxTabWidth = this.config.maxTabWidth || 250;
38670 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38671 ts.monitorResize = false;
38672 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38673 ts.bodyEl.addClass('roo-layout-tabs-body');
38674 this.panels.each(this.initPanelAsTab, this);
38677 initPanelAsTab : function(panel){
38678 var ti = this.tabs.addTab(
38682 this.config.closeOnTab && panel.isClosable(),
38685 if(panel.tabTip !== undefined){
38686 ti.setTooltip(panel.tabTip);
38688 ti.on("activate", function(){
38689 this.setActivePanel(panel);
38692 if(this.config.closeOnTab){
38693 ti.on("beforeclose", function(t, e){
38695 this.remove(panel);
38699 panel.tabItem = ti;
38704 updatePanelTitle : function(panel, title)
38706 if(this.activePanel == panel){
38707 this.updateTitle(title);
38710 var ti = this.tabs.getTab(panel.getEl().id);
38712 if(panel.tabTip !== undefined){
38713 ti.setTooltip(panel.tabTip);
38718 updateTitle : function(title){
38719 if(this.titleTextEl && !this.config.title){
38720 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38724 setActivePanel : function(panel)
38726 panel = this.getPanel(panel);
38727 if(this.activePanel && this.activePanel != panel){
38728 if(this.activePanel.setActiveState(false) === false){
38732 this.activePanel = panel;
38733 panel.setActiveState(true);
38734 if(this.panelSize){
38735 panel.setSize(this.panelSize.width, this.panelSize.height);
38738 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38740 this.updateTitle(panel.getTitle());
38742 this.fireEvent("invalidated", this);
38744 this.fireEvent("panelactivated", this, panel);
38748 * Shows the specified panel.
38749 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38750 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38752 showPanel : function(panel)
38754 panel = this.getPanel(panel);
38757 var tab = this.tabs.getTab(panel.getEl().id);
38758 if(tab.isHidden()){
38759 this.tabs.unhideTab(tab.id);
38763 this.setActivePanel(panel);
38770 * Get the active panel for this region.
38771 * @return {Roo.ContentPanel} The active panel or null
38773 getActivePanel : function(){
38774 return this.activePanel;
38777 validateVisibility : function(){
38778 if(this.panels.getCount() < 1){
38779 this.updateTitle(" ");
38780 this.closeBtn.hide();
38783 if(!this.isVisible()){
38790 * Adds the passed ContentPanel(s) to this region.
38791 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38792 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38794 add : function(panel)
38796 if(arguments.length > 1){
38797 for(var i = 0, len = arguments.length; i < len; i++) {
38798 this.add(arguments[i]);
38803 // if we have not been rendered yet, then we can not really do much of this..
38804 if (!this.bodyEl) {
38805 this.unrendered_panels.push(panel);
38812 if(this.hasPanel(panel)){
38813 this.showPanel(panel);
38816 panel.setRegion(this);
38817 this.panels.add(panel);
38818 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38819 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38820 // and hide them... ???
38821 this.bodyEl.dom.appendChild(panel.getEl().dom);
38822 if(panel.background !== true){
38823 this.setActivePanel(panel);
38825 this.fireEvent("paneladded", this, panel);
38832 this.initPanelAsTab(panel);
38836 if(panel.background !== true){
38837 this.tabs.activate(panel.getEl().id);
38839 this.fireEvent("paneladded", this, panel);
38844 * Hides the tab for the specified panel.
38845 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38847 hidePanel : function(panel){
38848 if(this.tabs && (panel = this.getPanel(panel))){
38849 this.tabs.hideTab(panel.getEl().id);
38854 * Unhides the tab for a previously hidden panel.
38855 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38857 unhidePanel : function(panel){
38858 if(this.tabs && (panel = this.getPanel(panel))){
38859 this.tabs.unhideTab(panel.getEl().id);
38863 clearPanels : function(){
38864 while(this.panels.getCount() > 0){
38865 this.remove(this.panels.first());
38870 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38871 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38872 * @param {Boolean} preservePanel Overrides the config preservePanel option
38873 * @return {Roo.ContentPanel} The panel that was removed
38875 remove : function(panel, preservePanel)
38877 panel = this.getPanel(panel);
38882 this.fireEvent("beforeremove", this, panel, e);
38883 if(e.cancel === true){
38886 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38887 var panelId = panel.getId();
38888 this.panels.removeKey(panelId);
38890 document.body.appendChild(panel.getEl().dom);
38893 this.tabs.removeTab(panel.getEl().id);
38894 }else if (!preservePanel){
38895 this.bodyEl.dom.removeChild(panel.getEl().dom);
38897 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38898 var p = this.panels.first();
38899 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38900 tempEl.appendChild(p.getEl().dom);
38901 this.bodyEl.update("");
38902 this.bodyEl.dom.appendChild(p.getEl().dom);
38904 this.updateTitle(p.getTitle());
38906 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38907 this.setActivePanel(p);
38909 panel.setRegion(null);
38910 if(this.activePanel == panel){
38911 this.activePanel = null;
38913 if(this.config.autoDestroy !== false && preservePanel !== true){
38914 try{panel.destroy();}catch(e){}
38916 this.fireEvent("panelremoved", this, panel);
38921 * Returns the TabPanel component used by this region
38922 * @return {Roo.TabPanel}
38924 getTabs : function(){
38928 createTool : function(parentEl, className){
38929 var btn = Roo.DomHelper.append(parentEl, {
38931 cls: "x-layout-tools-button",
38934 cls: "roo-layout-tools-button-inner " + className,
38938 btn.addClassOnOver("roo-layout-tools-button-over");
38943 * Ext JS Library 1.1.1
38944 * Copyright(c) 2006-2007, Ext JS, LLC.
38946 * Originally Released Under LGPL - original licence link has changed is not relivant.
38949 * <script type="text/javascript">
38955 * @class Roo.SplitLayoutRegion
38956 * @extends Roo.LayoutRegion
38957 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38959 Roo.bootstrap.layout.Split = function(config){
38960 this.cursor = config.cursor;
38961 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38964 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38966 splitTip : "Drag to resize.",
38967 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38968 useSplitTips : false,
38970 applyConfig : function(config){
38971 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38974 onRender : function(ctr,pos) {
38976 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38977 if(!this.config.split){
38982 var splitEl = Roo.DomHelper.append(ctr.dom, {
38984 id: this.el.id + "-split",
38985 cls: "roo-layout-split roo-layout-split-"+this.position,
38988 /** The SplitBar for this region
38989 * @type Roo.SplitBar */
38990 // does not exist yet...
38991 Roo.log([this.position, this.orientation]);
38993 this.split = new Roo.bootstrap.SplitBar({
38994 dragElement : splitEl,
38995 resizingElement: this.el,
38996 orientation : this.orientation
38999 this.split.on("moved", this.onSplitMove, this);
39000 this.split.useShim = this.config.useShim === true;
39001 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39002 if(this.useSplitTips){
39003 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39005 //if(config.collapsible){
39006 // this.split.el.on("dblclick", this.collapse, this);
39009 if(typeof this.config.minSize != "undefined"){
39010 this.split.minSize = this.config.minSize;
39012 if(typeof this.config.maxSize != "undefined"){
39013 this.split.maxSize = this.config.maxSize;
39015 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39016 this.hideSplitter();
39021 getHMaxSize : function(){
39022 var cmax = this.config.maxSize || 10000;
39023 var center = this.mgr.getRegion("center");
39024 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39027 getVMaxSize : function(){
39028 var cmax = this.config.maxSize || 10000;
39029 var center = this.mgr.getRegion("center");
39030 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39033 onSplitMove : function(split, newSize){
39034 this.fireEvent("resized", this, newSize);
39038 * Returns the {@link Roo.SplitBar} for this region.
39039 * @return {Roo.SplitBar}
39041 getSplitBar : function(){
39046 this.hideSplitter();
39047 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39050 hideSplitter : function(){
39052 this.split.el.setLocation(-2000,-2000);
39053 this.split.el.hide();
39059 this.split.el.show();
39061 Roo.bootstrap.layout.Split.superclass.show.call(this);
39064 beforeSlide: function(){
39065 if(Roo.isGecko){// firefox overflow auto bug workaround
39066 this.bodyEl.clip();
39068 this.tabs.bodyEl.clip();
39070 if(this.activePanel){
39071 this.activePanel.getEl().clip();
39073 if(this.activePanel.beforeSlide){
39074 this.activePanel.beforeSlide();
39080 afterSlide : function(){
39081 if(Roo.isGecko){// firefox overflow auto bug workaround
39082 this.bodyEl.unclip();
39084 this.tabs.bodyEl.unclip();
39086 if(this.activePanel){
39087 this.activePanel.getEl().unclip();
39088 if(this.activePanel.afterSlide){
39089 this.activePanel.afterSlide();
39095 initAutoHide : function(){
39096 if(this.autoHide !== false){
39097 if(!this.autoHideHd){
39098 var st = new Roo.util.DelayedTask(this.slideIn, this);
39099 this.autoHideHd = {
39100 "mouseout": function(e){
39101 if(!e.within(this.el, true)){
39105 "mouseover" : function(e){
39111 this.el.on(this.autoHideHd);
39115 clearAutoHide : function(){
39116 if(this.autoHide !== false){
39117 this.el.un("mouseout", this.autoHideHd.mouseout);
39118 this.el.un("mouseover", this.autoHideHd.mouseover);
39122 clearMonitor : function(){
39123 Roo.get(document).un("click", this.slideInIf, this);
39126 // these names are backwards but not changed for compat
39127 slideOut : function(){
39128 if(this.isSlid || this.el.hasActiveFx()){
39131 this.isSlid = true;
39132 if(this.collapseBtn){
39133 this.collapseBtn.hide();
39135 this.closeBtnState = this.closeBtn.getStyle('display');
39136 this.closeBtn.hide();
39138 this.stickBtn.show();
39141 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39142 this.beforeSlide();
39143 this.el.setStyle("z-index", 10001);
39144 this.el.slideIn(this.getSlideAnchor(), {
39145 callback: function(){
39147 this.initAutoHide();
39148 Roo.get(document).on("click", this.slideInIf, this);
39149 this.fireEvent("slideshow", this);
39156 afterSlideIn : function(){
39157 this.clearAutoHide();
39158 this.isSlid = false;
39159 this.clearMonitor();
39160 this.el.setStyle("z-index", "");
39161 if(this.collapseBtn){
39162 this.collapseBtn.show();
39164 this.closeBtn.setStyle('display', this.closeBtnState);
39166 this.stickBtn.hide();
39168 this.fireEvent("slidehide", this);
39171 slideIn : function(cb){
39172 if(!this.isSlid || this.el.hasActiveFx()){
39176 this.isSlid = false;
39177 this.beforeSlide();
39178 this.el.slideOut(this.getSlideAnchor(), {
39179 callback: function(){
39180 this.el.setLeftTop(-10000, -10000);
39182 this.afterSlideIn();
39190 slideInIf : function(e){
39191 if(!e.within(this.el)){
39196 animateCollapse : function(){
39197 this.beforeSlide();
39198 this.el.setStyle("z-index", 20000);
39199 var anchor = this.getSlideAnchor();
39200 this.el.slideOut(anchor, {
39201 callback : function(){
39202 this.el.setStyle("z-index", "");
39203 this.collapsedEl.slideIn(anchor, {duration:.3});
39205 this.el.setLocation(-10000,-10000);
39207 this.fireEvent("collapsed", this);
39214 animateExpand : function(){
39215 this.beforeSlide();
39216 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39217 this.el.setStyle("z-index", 20000);
39218 this.collapsedEl.hide({
39221 this.el.slideIn(this.getSlideAnchor(), {
39222 callback : function(){
39223 this.el.setStyle("z-index", "");
39226 this.split.el.show();
39228 this.fireEvent("invalidated", this);
39229 this.fireEvent("expanded", this);
39257 getAnchor : function(){
39258 return this.anchors[this.position];
39261 getCollapseAnchor : function(){
39262 return this.canchors[this.position];
39265 getSlideAnchor : function(){
39266 return this.sanchors[this.position];
39269 getAlignAdj : function(){
39270 var cm = this.cmargins;
39271 switch(this.position){
39287 getExpandAdj : function(){
39288 var c = this.collapsedEl, cm = this.cmargins;
39289 switch(this.position){
39291 return [-(cm.right+c.getWidth()+cm.left), 0];
39294 return [cm.right+c.getWidth()+cm.left, 0];
39297 return [0, -(cm.top+cm.bottom+c.getHeight())];
39300 return [0, cm.top+cm.bottom+c.getHeight()];
39306 * Ext JS Library 1.1.1
39307 * Copyright(c) 2006-2007, Ext JS, LLC.
39309 * Originally Released Under LGPL - original licence link has changed is not relivant.
39312 * <script type="text/javascript">
39315 * These classes are private internal classes
39317 Roo.bootstrap.layout.Center = function(config){
39318 config.region = "center";
39319 Roo.bootstrap.layout.Region.call(this, config);
39320 this.visible = true;
39321 this.minWidth = config.minWidth || 20;
39322 this.minHeight = config.minHeight || 20;
39325 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39327 // center panel can't be hidden
39331 // center panel can't be hidden
39334 getMinWidth: function(){
39335 return this.minWidth;
39338 getMinHeight: function(){
39339 return this.minHeight;
39353 Roo.bootstrap.layout.North = function(config)
39355 config.region = 'north';
39356 config.cursor = 'n-resize';
39358 Roo.bootstrap.layout.Split.call(this, config);
39362 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39363 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39364 this.split.el.addClass("roo-layout-split-v");
39366 //var size = config.initialSize || config.height;
39367 //if(this.el && typeof size != "undefined"){
39368 // this.el.setHeight(size);
39371 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39373 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39376 onRender : function(ctr, pos)
39378 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39379 var size = this.config.initialSize || this.config.height;
39380 if(this.el && typeof size != "undefined"){
39381 this.el.setHeight(size);
39386 getBox : function(){
39387 if(this.collapsed){
39388 return this.collapsedEl.getBox();
39390 var box = this.el.getBox();
39392 box.height += this.split.el.getHeight();
39397 updateBox : function(box){
39398 if(this.split && !this.collapsed){
39399 box.height -= this.split.el.getHeight();
39400 this.split.el.setLeft(box.x);
39401 this.split.el.setTop(box.y+box.height);
39402 this.split.el.setWidth(box.width);
39404 if(this.collapsed){
39405 this.updateBody(box.width, null);
39407 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39415 Roo.bootstrap.layout.South = function(config){
39416 config.region = 'south';
39417 config.cursor = 's-resize';
39418 Roo.bootstrap.layout.Split.call(this, config);
39420 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39421 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39422 this.split.el.addClass("roo-layout-split-v");
39427 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39428 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39430 onRender : function(ctr, pos)
39432 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39433 var size = this.config.initialSize || this.config.height;
39434 if(this.el && typeof size != "undefined"){
39435 this.el.setHeight(size);
39440 getBox : function(){
39441 if(this.collapsed){
39442 return this.collapsedEl.getBox();
39444 var box = this.el.getBox();
39446 var sh = this.split.el.getHeight();
39453 updateBox : function(box){
39454 if(this.split && !this.collapsed){
39455 var sh = this.split.el.getHeight();
39458 this.split.el.setLeft(box.x);
39459 this.split.el.setTop(box.y-sh);
39460 this.split.el.setWidth(box.width);
39462 if(this.collapsed){
39463 this.updateBody(box.width, null);
39465 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39469 Roo.bootstrap.layout.East = function(config){
39470 config.region = "east";
39471 config.cursor = "e-resize";
39472 Roo.bootstrap.layout.Split.call(this, config);
39474 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39475 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39476 this.split.el.addClass("roo-layout-split-h");
39480 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39481 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39483 onRender : function(ctr, pos)
39485 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39486 var size = this.config.initialSize || this.config.width;
39487 if(this.el && typeof size != "undefined"){
39488 this.el.setWidth(size);
39493 getBox : function(){
39494 if(this.collapsed){
39495 return this.collapsedEl.getBox();
39497 var box = this.el.getBox();
39499 var sw = this.split.el.getWidth();
39506 updateBox : function(box){
39507 if(this.split && !this.collapsed){
39508 var sw = this.split.el.getWidth();
39510 this.split.el.setLeft(box.x);
39511 this.split.el.setTop(box.y);
39512 this.split.el.setHeight(box.height);
39515 if(this.collapsed){
39516 this.updateBody(null, box.height);
39518 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39522 Roo.bootstrap.layout.West = function(config){
39523 config.region = "west";
39524 config.cursor = "w-resize";
39526 Roo.bootstrap.layout.Split.call(this, config);
39528 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39529 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39530 this.split.el.addClass("roo-layout-split-h");
39534 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39535 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39537 onRender: function(ctr, pos)
39539 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39540 var size = this.config.initialSize || this.config.width;
39541 if(typeof size != "undefined"){
39542 this.el.setWidth(size);
39546 getBox : function(){
39547 if(this.collapsed){
39548 return this.collapsedEl.getBox();
39550 var box = this.el.getBox();
39551 if (box.width == 0) {
39552 box.width = this.config.width; // kludge?
39555 box.width += this.split.el.getWidth();
39560 updateBox : function(box){
39561 if(this.split && !this.collapsed){
39562 var sw = this.split.el.getWidth();
39564 this.split.el.setLeft(box.x+box.width);
39565 this.split.el.setTop(box.y);
39566 this.split.el.setHeight(box.height);
39568 if(this.collapsed){
39569 this.updateBody(null, box.height);
39571 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39573 });Roo.namespace("Roo.bootstrap.panel");/*
39575 * Ext JS Library 1.1.1
39576 * Copyright(c) 2006-2007, Ext JS, LLC.
39578 * Originally Released Under LGPL - original licence link has changed is not relivant.
39581 * <script type="text/javascript">
39584 * @class Roo.ContentPanel
39585 * @extends Roo.util.Observable
39586 * A basic ContentPanel element.
39587 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39588 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39589 * @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
39590 * @cfg {Boolean} closable True if the panel can be closed/removed
39591 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39592 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39593 * @cfg {Toolbar} toolbar A toolbar for this panel
39594 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39595 * @cfg {String} title The title for this panel
39596 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39597 * @cfg {String} url Calls {@link #setUrl} with this value
39598 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39599 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39600 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39601 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39602 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39603 * @cfg {Boolean} badges render the badges
39604 * @cfg {String} cls extra classes to use
39605 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39608 * Create a new ContentPanel.
39609 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39610 * @param {String/Object} config A string to set only the title or a config object
39611 * @param {String} content (optional) Set the HTML content for this panel
39612 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39614 Roo.bootstrap.panel.Content = function( config){
39616 this.tpl = config.tpl || false;
39618 var el = config.el;
39619 var content = config.content;
39621 if(config.autoCreate){ // xtype is available if this is called from factory
39624 this.el = Roo.get(el);
39625 if(!this.el && config && config.autoCreate){
39626 if(typeof config.autoCreate == "object"){
39627 if(!config.autoCreate.id){
39628 config.autoCreate.id = config.id||el;
39630 this.el = Roo.DomHelper.append(document.body,
39631 config.autoCreate, true);
39635 cls: (config.cls || '') +
39636 (config.background ? ' bg-' + config.background : '') +
39637 " roo-layout-inactive-content",
39640 if (config.iframe) {
39644 style : 'border: 0px',
39645 src : 'about:blank'
39651 elcfg.html = config.html;
39655 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39656 if (config.iframe) {
39657 this.iframeEl = this.el.select('iframe',true).first();
39662 this.closable = false;
39663 this.loaded = false;
39664 this.active = false;
39667 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39669 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39671 this.wrapEl = this.el; //this.el.wrap();
39673 if (config.toolbar.items) {
39674 ti = config.toolbar.items ;
39675 delete config.toolbar.items ;
39679 this.toolbar.render(this.wrapEl, 'before');
39680 for(var i =0;i < ti.length;i++) {
39681 // Roo.log(['add child', items[i]]);
39682 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39684 this.toolbar.items = nitems;
39685 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39686 delete config.toolbar;
39690 // xtype created footer. - not sure if will work as we normally have to render first..
39691 if (this.footer && !this.footer.el && this.footer.xtype) {
39692 if (!this.wrapEl) {
39693 this.wrapEl = this.el.wrap();
39696 this.footer.container = this.wrapEl.createChild();
39698 this.footer = Roo.factory(this.footer, Roo);
39703 if(typeof config == "string"){
39704 this.title = config;
39706 Roo.apply(this, config);
39710 this.resizeEl = Roo.get(this.resizeEl, true);
39712 this.resizeEl = this.el;
39714 // handle view.xtype
39722 * Fires when this panel is activated.
39723 * @param {Roo.ContentPanel} this
39727 * @event deactivate
39728 * Fires when this panel is activated.
39729 * @param {Roo.ContentPanel} this
39731 "deactivate" : true,
39735 * Fires when this panel is resized if fitToFrame is true.
39736 * @param {Roo.ContentPanel} this
39737 * @param {Number} width The width after any component adjustments
39738 * @param {Number} height The height after any component adjustments
39744 * Fires when this tab is created
39745 * @param {Roo.ContentPanel} this
39756 if(this.autoScroll && !this.iframe){
39757 this.resizeEl.setStyle("overflow", "auto");
39759 // fix randome scrolling
39760 //this.el.on('scroll', function() {
39761 // Roo.log('fix random scolling');
39762 // this.scrollTo('top',0);
39765 content = content || this.content;
39767 this.setContent(content);
39769 if(config && config.url){
39770 this.setUrl(this.url, this.params, this.loadOnce);
39775 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39777 if (this.view && typeof(this.view.xtype) != 'undefined') {
39778 this.view.el = this.el.appendChild(document.createElement("div"));
39779 this.view = Roo.factory(this.view);
39780 this.view.render && this.view.render(false, '');
39784 this.fireEvent('render', this);
39787 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39797 setRegion : function(region){
39798 this.region = region;
39799 this.setActiveClass(region && !this.background);
39803 setActiveClass: function(state)
39806 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39807 this.el.setStyle('position','relative');
39809 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39810 this.el.setStyle('position', 'absolute');
39815 * Returns the toolbar for this Panel if one was configured.
39816 * @return {Roo.Toolbar}
39818 getToolbar : function(){
39819 return this.toolbar;
39822 setActiveState : function(active)
39824 this.active = active;
39825 this.setActiveClass(active);
39827 if(this.fireEvent("deactivate", this) === false){
39832 this.fireEvent("activate", this);
39836 * Updates this panel's element (not for iframe)
39837 * @param {String} content The new content
39838 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39840 setContent : function(content, loadScripts){
39845 this.el.update(content, loadScripts);
39848 ignoreResize : function(w, h){
39849 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39852 this.lastSize = {width: w, height: h};
39857 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39858 * @return {Roo.UpdateManager} The UpdateManager
39860 getUpdateManager : function(){
39864 return this.el.getUpdateManager();
39867 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39868 * Does not work with IFRAME contents
39869 * @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:
39872 url: "your-url.php",
39873 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39874 callback: yourFunction,
39875 scope: yourObject, //(optional scope)
39878 text: "Loading...",
39884 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39885 * 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.
39886 * @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}
39887 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39888 * @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.
39889 * @return {Roo.ContentPanel} this
39897 var um = this.el.getUpdateManager();
39898 um.update.apply(um, arguments);
39904 * 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.
39905 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39906 * @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)
39907 * @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)
39908 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39910 setUrl : function(url, params, loadOnce){
39912 this.iframeEl.dom.src = url;
39916 if(this.refreshDelegate){
39917 this.removeListener("activate", this.refreshDelegate);
39919 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39920 this.on("activate", this.refreshDelegate);
39921 return this.el.getUpdateManager();
39924 _handleRefresh : function(url, params, loadOnce){
39925 if(!loadOnce || !this.loaded){
39926 var updater = this.el.getUpdateManager();
39927 updater.update(url, params, this._setLoaded.createDelegate(this));
39931 _setLoaded : function(){
39932 this.loaded = true;
39936 * Returns this panel's id
39939 getId : function(){
39944 * Returns this panel's element - used by regiosn to add.
39945 * @return {Roo.Element}
39947 getEl : function(){
39948 return this.wrapEl || this.el;
39953 adjustForComponents : function(width, height)
39955 //Roo.log('adjustForComponents ');
39956 if(this.resizeEl != this.el){
39957 width -= this.el.getFrameWidth('lr');
39958 height -= this.el.getFrameWidth('tb');
39961 var te = this.toolbar.getEl();
39962 te.setWidth(width);
39963 height -= te.getHeight();
39966 var te = this.footer.getEl();
39967 te.setWidth(width);
39968 height -= te.getHeight();
39972 if(this.adjustments){
39973 width += this.adjustments[0];
39974 height += this.adjustments[1];
39976 return {"width": width, "height": height};
39979 setSize : function(width, height){
39980 if(this.fitToFrame && !this.ignoreResize(width, height)){
39981 if(this.fitContainer && this.resizeEl != this.el){
39982 this.el.setSize(width, height);
39984 var size = this.adjustForComponents(width, height);
39986 this.iframeEl.setSize(width,height);
39989 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39990 this.fireEvent('resize', this, size.width, size.height);
39997 * Returns this panel's title
40000 getTitle : function(){
40002 if (typeof(this.title) != 'object') {
40007 for (var k in this.title) {
40008 if (!this.title.hasOwnProperty(k)) {
40012 if (k.indexOf('-') >= 0) {
40013 var s = k.split('-');
40014 for (var i = 0; i<s.length; i++) {
40015 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40018 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40025 * Set this panel's title
40026 * @param {String} title
40028 setTitle : function(title){
40029 this.title = title;
40031 this.region.updatePanelTitle(this, title);
40036 * Returns true is this panel was configured to be closable
40037 * @return {Boolean}
40039 isClosable : function(){
40040 return this.closable;
40043 beforeSlide : function(){
40045 this.resizeEl.clip();
40048 afterSlide : function(){
40050 this.resizeEl.unclip();
40054 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40055 * Will fail silently if the {@link #setUrl} method has not been called.
40056 * This does not activate the panel, just updates its content.
40058 refresh : function(){
40059 if(this.refreshDelegate){
40060 this.loaded = false;
40061 this.refreshDelegate();
40066 * Destroys this panel
40068 destroy : function(){
40069 this.el.removeAllListeners();
40070 var tempEl = document.createElement("span");
40071 tempEl.appendChild(this.el.dom);
40072 tempEl.innerHTML = "";
40078 * form - if the content panel contains a form - this is a reference to it.
40079 * @type {Roo.form.Form}
40083 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40084 * This contains a reference to it.
40090 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40100 * @param {Object} cfg Xtype definition of item to add.
40104 getChildContainer: function () {
40105 return this.getEl();
40110 var ret = new Roo.factory(cfg);
40115 if (cfg.xtype.match(/^Form$/)) {
40118 //if (this.footer) {
40119 // el = this.footer.container.insertSibling(false, 'before');
40121 el = this.el.createChild();
40124 this.form = new Roo.form.Form(cfg);
40127 if ( this.form.allItems.length) {
40128 this.form.render(el.dom);
40132 // should only have one of theses..
40133 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40134 // views.. should not be just added - used named prop 'view''
40136 cfg.el = this.el.appendChild(document.createElement("div"));
40139 var ret = new Roo.factory(cfg);
40141 ret.render && ret.render(false, ''); // render blank..
40151 * @class Roo.bootstrap.panel.Grid
40152 * @extends Roo.bootstrap.panel.Content
40154 * Create a new GridPanel.
40155 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40156 * @param {Object} config A the config object
40162 Roo.bootstrap.panel.Grid = function(config)
40166 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40167 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40169 config.el = this.wrapper;
40170 //this.el = this.wrapper;
40172 if (config.container) {
40173 // ctor'ed from a Border/panel.grid
40176 this.wrapper.setStyle("overflow", "hidden");
40177 this.wrapper.addClass('roo-grid-container');
40182 if(config.toolbar){
40183 var tool_el = this.wrapper.createChild();
40184 this.toolbar = Roo.factory(config.toolbar);
40186 if (config.toolbar.items) {
40187 ti = config.toolbar.items ;
40188 delete config.toolbar.items ;
40192 this.toolbar.render(tool_el);
40193 for(var i =0;i < ti.length;i++) {
40194 // Roo.log(['add child', items[i]]);
40195 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40197 this.toolbar.items = nitems;
40199 delete config.toolbar;
40202 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40203 config.grid.scrollBody = true;;
40204 config.grid.monitorWindowResize = false; // turn off autosizing
40205 config.grid.autoHeight = false;
40206 config.grid.autoWidth = false;
40208 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40210 if (config.background) {
40211 // render grid on panel activation (if panel background)
40212 this.on('activate', function(gp) {
40213 if (!gp.grid.rendered) {
40214 gp.grid.render(this.wrapper);
40215 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40220 this.grid.render(this.wrapper);
40221 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40224 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40225 // ??? needed ??? config.el = this.wrapper;
40230 // xtype created footer. - not sure if will work as we normally have to render first..
40231 if (this.footer && !this.footer.el && this.footer.xtype) {
40233 var ctr = this.grid.getView().getFooterPanel(true);
40234 this.footer.dataSource = this.grid.dataSource;
40235 this.footer = Roo.factory(this.footer, Roo);
40236 this.footer.render(ctr);
40246 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40247 getId : function(){
40248 return this.grid.id;
40252 * Returns the grid for this panel
40253 * @return {Roo.bootstrap.Table}
40255 getGrid : function(){
40259 setSize : function(width, height){
40260 if(!this.ignoreResize(width, height)){
40261 var grid = this.grid;
40262 var size = this.adjustForComponents(width, height);
40263 // tfoot is not a footer?
40266 var gridel = grid.getGridEl();
40267 gridel.setSize(size.width, size.height);
40269 var tbd = grid.getGridEl().select('tbody', true).first();
40270 var thd = grid.getGridEl().select('thead',true).first();
40271 var tbf= grid.getGridEl().select('tfoot', true).first();
40274 size.height -= tbf.getHeight();
40277 size.height -= thd.getHeight();
40280 tbd.setSize(size.width, size.height );
40281 // this is for the account management tab -seems to work there.
40282 var thd = grid.getGridEl().select('thead',true).first();
40284 // tbd.setSize(size.width, size.height - thd.getHeight());
40293 beforeSlide : function(){
40294 this.grid.getView().scroller.clip();
40297 afterSlide : function(){
40298 this.grid.getView().scroller.unclip();
40301 destroy : function(){
40302 this.grid.destroy();
40304 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40309 * @class Roo.bootstrap.panel.Nest
40310 * @extends Roo.bootstrap.panel.Content
40312 * Create a new Panel, that can contain a layout.Border.
40315 * @param {Roo.BorderLayout} layout The layout for this panel
40316 * @param {String/Object} config A string to set only the title or a config object
40318 Roo.bootstrap.panel.Nest = function(config)
40320 // construct with only one argument..
40321 /* FIXME - implement nicer consturctors
40322 if (layout.layout) {
40324 layout = config.layout;
40325 delete config.layout;
40327 if (layout.xtype && !layout.getEl) {
40328 // then layout needs constructing..
40329 layout = Roo.factory(layout, Roo);
40333 config.el = config.layout.getEl();
40335 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40337 config.layout.monitorWindowResize = false; // turn off autosizing
40338 this.layout = config.layout;
40339 this.layout.getEl().addClass("roo-layout-nested-layout");
40340 this.layout.parent = this;
40347 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40349 setSize : function(width, height){
40350 if(!this.ignoreResize(width, height)){
40351 var size = this.adjustForComponents(width, height);
40352 var el = this.layout.getEl();
40353 if (size.height < 1) {
40354 el.setWidth(size.width);
40356 el.setSize(size.width, size.height);
40358 var touch = el.dom.offsetWidth;
40359 this.layout.layout();
40360 // ie requires a double layout on the first pass
40361 if(Roo.isIE && !this.initialized){
40362 this.initialized = true;
40363 this.layout.layout();
40368 // activate all subpanels if not currently active..
40370 setActiveState : function(active){
40371 this.active = active;
40372 this.setActiveClass(active);
40375 this.fireEvent("deactivate", this);
40379 this.fireEvent("activate", this);
40380 // not sure if this should happen before or after..
40381 if (!this.layout) {
40382 return; // should not happen..
40385 for (var r in this.layout.regions) {
40386 reg = this.layout.getRegion(r);
40387 if (reg.getActivePanel()) {
40388 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40389 reg.setActivePanel(reg.getActivePanel());
40392 if (!reg.panels.length) {
40395 reg.showPanel(reg.getPanel(0));
40404 * Returns the nested BorderLayout for this panel
40405 * @return {Roo.BorderLayout}
40407 getLayout : function(){
40408 return this.layout;
40412 * Adds a xtype elements to the layout of the nested panel
40416 xtype : 'ContentPanel',
40423 xtype : 'NestedLayoutPanel',
40429 items : [ ... list of content panels or nested layout panels.. ]
40433 * @param {Object} cfg Xtype definition of item to add.
40435 addxtype : function(cfg) {
40436 return this.layout.addxtype(cfg);
40441 * Ext JS Library 1.1.1
40442 * Copyright(c) 2006-2007, Ext JS, LLC.
40444 * Originally Released Under LGPL - original licence link has changed is not relivant.
40447 * <script type="text/javascript">
40450 * @class Roo.TabPanel
40451 * @extends Roo.util.Observable
40452 * A lightweight tab container.
40456 // basic tabs 1, built from existing content
40457 var tabs = new Roo.TabPanel("tabs1");
40458 tabs.addTab("script", "View Script");
40459 tabs.addTab("markup", "View Markup");
40460 tabs.activate("script");
40462 // more advanced tabs, built from javascript
40463 var jtabs = new Roo.TabPanel("jtabs");
40464 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40466 // set up the UpdateManager
40467 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40468 var updater = tab2.getUpdateManager();
40469 updater.setDefaultUrl("ajax1.htm");
40470 tab2.on('activate', updater.refresh, updater, true);
40472 // Use setUrl for Ajax loading
40473 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40474 tab3.setUrl("ajax2.htm", null, true);
40477 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40480 jtabs.activate("jtabs-1");
40483 * Create a new TabPanel.
40484 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40485 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40487 Roo.bootstrap.panel.Tabs = function(config){
40489 * The container element for this TabPanel.
40490 * @type Roo.Element
40492 this.el = Roo.get(config.el);
40495 if(typeof config == "boolean"){
40496 this.tabPosition = config ? "bottom" : "top";
40498 Roo.apply(this, config);
40502 if(this.tabPosition == "bottom"){
40503 // if tabs are at the bottom = create the body first.
40504 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40505 this.el.addClass("roo-tabs-bottom");
40507 // next create the tabs holders
40509 if (this.tabPosition == "west"){
40511 var reg = this.region; // fake it..
40513 if (!reg.mgr.parent) {
40516 reg = reg.mgr.parent.region;
40518 Roo.log("got nest?");
40520 if (reg.mgr.getRegion('west')) {
40521 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40522 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40523 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40524 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40525 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40533 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40534 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40535 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40536 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40541 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40544 // finally - if tabs are at the top, then create the body last..
40545 if(this.tabPosition != "bottom"){
40546 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40547 * @type Roo.Element
40549 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40550 this.el.addClass("roo-tabs-top");
40554 this.bodyEl.setStyle("position", "relative");
40556 this.active = null;
40557 this.activateDelegate = this.activate.createDelegate(this);
40562 * Fires when the active tab changes
40563 * @param {Roo.TabPanel} this
40564 * @param {Roo.TabPanelItem} activePanel The new active tab
40568 * @event beforetabchange
40569 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40570 * @param {Roo.TabPanel} this
40571 * @param {Object} e Set cancel to true on this object to cancel the tab change
40572 * @param {Roo.TabPanelItem} tab The tab being changed to
40574 "beforetabchange" : true
40577 Roo.EventManager.onWindowResize(this.onResize, this);
40578 this.cpad = this.el.getPadding("lr");
40579 this.hiddenCount = 0;
40582 // toolbar on the tabbar support...
40583 if (this.toolbar) {
40584 alert("no toolbar support yet");
40585 this.toolbar = false;
40587 var tcfg = this.toolbar;
40588 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40589 this.toolbar = new Roo.Toolbar(tcfg);
40590 if (Roo.isSafari) {
40591 var tbl = tcfg.container.child('table', true);
40592 tbl.setAttribute('width', '100%');
40600 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40603 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40605 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40607 tabPosition : "top",
40609 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40611 currentTabWidth : 0,
40613 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40617 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40621 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40623 preferredTabWidth : 175,
40625 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40627 resizeTabs : false,
40629 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40631 monitorResize : true,
40633 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40635 toolbar : false, // set by caller..
40637 region : false, /// set by caller
40639 disableTooltips : true, // not used yet...
40642 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40643 * @param {String} id The id of the div to use <b>or create</b>
40644 * @param {String} text The text for the tab
40645 * @param {String} content (optional) Content to put in the TabPanelItem body
40646 * @param {Boolean} closable (optional) True to create a close icon on the tab
40647 * @return {Roo.TabPanelItem} The created TabPanelItem
40649 addTab : function(id, text, content, closable, tpl)
40651 var item = new Roo.bootstrap.panel.TabItem({
40655 closable : closable,
40658 this.addTabItem(item);
40660 item.setContent(content);
40666 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40667 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40668 * @return {Roo.TabPanelItem}
40670 getTab : function(id){
40671 return this.items[id];
40675 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40676 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40678 hideTab : function(id){
40679 var t = this.items[id];
40682 this.hiddenCount++;
40683 this.autoSizeTabs();
40688 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40689 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40691 unhideTab : function(id){
40692 var t = this.items[id];
40694 t.setHidden(false);
40695 this.hiddenCount--;
40696 this.autoSizeTabs();
40701 * Adds an existing {@link Roo.TabPanelItem}.
40702 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40704 addTabItem : function(item)
40706 this.items[item.id] = item;
40707 this.items.push(item);
40708 this.autoSizeTabs();
40709 // if(this.resizeTabs){
40710 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40711 // this.autoSizeTabs();
40713 // item.autoSize();
40718 * Removes a {@link Roo.TabPanelItem}.
40719 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40721 removeTab : function(id){
40722 var items = this.items;
40723 var tab = items[id];
40724 if(!tab) { return; }
40725 var index = items.indexOf(tab);
40726 if(this.active == tab && items.length > 1){
40727 var newTab = this.getNextAvailable(index);
40732 this.stripEl.dom.removeChild(tab.pnode.dom);
40733 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40734 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40736 items.splice(index, 1);
40737 delete this.items[tab.id];
40738 tab.fireEvent("close", tab);
40739 tab.purgeListeners();
40740 this.autoSizeTabs();
40743 getNextAvailable : function(start){
40744 var items = this.items;
40746 // look for a next tab that will slide over to
40747 // replace the one being removed
40748 while(index < items.length){
40749 var item = items[++index];
40750 if(item && !item.isHidden()){
40754 // if one isn't found select the previous tab (on the left)
40757 var item = items[--index];
40758 if(item && !item.isHidden()){
40766 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40767 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40769 disableTab : function(id){
40770 var tab = this.items[id];
40771 if(tab && this.active != tab){
40777 * Enables a {@link Roo.TabPanelItem} that is disabled.
40778 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40780 enableTab : function(id){
40781 var tab = this.items[id];
40786 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40787 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40788 * @return {Roo.TabPanelItem} The TabPanelItem.
40790 activate : function(id)
40792 //Roo.log('activite:' + id);
40794 var tab = this.items[id];
40798 if(tab == this.active || tab.disabled){
40802 this.fireEvent("beforetabchange", this, e, tab);
40803 if(e.cancel !== true && !tab.disabled){
40805 this.active.hide();
40807 this.active = this.items[id];
40808 this.active.show();
40809 this.fireEvent("tabchange", this, this.active);
40815 * Gets the active {@link Roo.TabPanelItem}.
40816 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40818 getActiveTab : function(){
40819 return this.active;
40823 * Updates the tab body element to fit the height of the container element
40824 * for overflow scrolling
40825 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40827 syncHeight : function(targetHeight){
40828 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40829 var bm = this.bodyEl.getMargins();
40830 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40831 this.bodyEl.setHeight(newHeight);
40835 onResize : function(){
40836 if(this.monitorResize){
40837 this.autoSizeTabs();
40842 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40844 beginUpdate : function(){
40845 this.updating = true;
40849 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40851 endUpdate : function(){
40852 this.updating = false;
40853 this.autoSizeTabs();
40857 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40859 autoSizeTabs : function()
40861 var count = this.items.length;
40862 var vcount = count - this.hiddenCount;
40865 this.stripEl.hide();
40867 this.stripEl.show();
40870 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40875 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40876 var availWidth = Math.floor(w / vcount);
40877 var b = this.stripBody;
40878 if(b.getWidth() > w){
40879 var tabs = this.items;
40880 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40881 if(availWidth < this.minTabWidth){
40882 /*if(!this.sleft){ // incomplete scrolling code
40883 this.createScrollButtons();
40886 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40889 if(this.currentTabWidth < this.preferredTabWidth){
40890 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40896 * Returns the number of tabs in this TabPanel.
40899 getCount : function(){
40900 return this.items.length;
40904 * Resizes all the tabs to the passed width
40905 * @param {Number} The new width
40907 setTabWidth : function(width){
40908 this.currentTabWidth = width;
40909 for(var i = 0, len = this.items.length; i < len; i++) {
40910 if(!this.items[i].isHidden()) {
40911 this.items[i].setWidth(width);
40917 * Destroys this TabPanel
40918 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40920 destroy : function(removeEl){
40921 Roo.EventManager.removeResizeListener(this.onResize, this);
40922 for(var i = 0, len = this.items.length; i < len; i++){
40923 this.items[i].purgeListeners();
40925 if(removeEl === true){
40926 this.el.update("");
40931 createStrip : function(container)
40933 var strip = document.createElement("nav");
40934 strip.className = Roo.bootstrap.version == 4 ?
40935 "navbar-light bg-light" :
40936 "navbar navbar-default"; //"x-tabs-wrap";
40937 container.appendChild(strip);
40941 createStripList : function(strip)
40943 // div wrapper for retard IE
40944 // returns the "tr" element.
40945 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40946 //'<div class="x-tabs-strip-wrap">'+
40947 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40948 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40949 return strip.firstChild; //.firstChild.firstChild.firstChild;
40951 createBody : function(container)
40953 var body = document.createElement("div");
40954 Roo.id(body, "tab-body");
40955 //Roo.fly(body).addClass("x-tabs-body");
40956 Roo.fly(body).addClass("tab-content");
40957 container.appendChild(body);
40960 createItemBody :function(bodyEl, id){
40961 var body = Roo.getDom(id);
40963 body = document.createElement("div");
40966 //Roo.fly(body).addClass("x-tabs-item-body");
40967 Roo.fly(body).addClass("tab-pane");
40968 bodyEl.insertBefore(body, bodyEl.firstChild);
40972 createStripElements : function(stripEl, text, closable, tpl)
40974 var td = document.createElement("li"); // was td..
40975 td.className = 'nav-item';
40977 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40980 stripEl.appendChild(td);
40982 td.className = "x-tabs-closable";
40983 if(!this.closeTpl){
40984 this.closeTpl = new Roo.Template(
40985 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40986 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40987 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40990 var el = this.closeTpl.overwrite(td, {"text": text});
40991 var close = el.getElementsByTagName("div")[0];
40992 var inner = el.getElementsByTagName("em")[0];
40993 return {"el": el, "close": close, "inner": inner};
40996 // not sure what this is..
40997 // if(!this.tabTpl){
40998 //this.tabTpl = new Roo.Template(
40999 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41000 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41002 // this.tabTpl = new Roo.Template(
41003 // '<a href="#">' +
41004 // '<span unselectable="on"' +
41005 // (this.disableTooltips ? '' : ' title="{text}"') +
41006 // ' >{text}</span></a>'
41012 var template = tpl || this.tabTpl || false;
41015 template = new Roo.Template(
41016 Roo.bootstrap.version == 4 ?
41018 '<a class="nav-link" href="#" unselectable="on"' +
41019 (this.disableTooltips ? '' : ' title="{text}"') +
41022 '<a class="nav-link" href="#">' +
41023 '<span unselectable="on"' +
41024 (this.disableTooltips ? '' : ' title="{text}"') +
41025 ' >{text}</span></a>'
41030 switch (typeof(template)) {
41034 template = new Roo.Template(template);
41040 var el = template.overwrite(td, {"text": text});
41042 var inner = el.getElementsByTagName("span")[0];
41044 return {"el": el, "inner": inner};
41052 * @class Roo.TabPanelItem
41053 * @extends Roo.util.Observable
41054 * Represents an individual item (tab plus body) in a TabPanel.
41055 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41056 * @param {String} id The id of this TabPanelItem
41057 * @param {String} text The text for the tab of this TabPanelItem
41058 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41060 Roo.bootstrap.panel.TabItem = function(config){
41062 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41063 * @type Roo.TabPanel
41065 this.tabPanel = config.panel;
41067 * The id for this TabPanelItem
41070 this.id = config.id;
41072 this.disabled = false;
41074 this.text = config.text;
41076 this.loaded = false;
41077 this.closable = config.closable;
41080 * The body element for this TabPanelItem.
41081 * @type Roo.Element
41083 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41084 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41085 this.bodyEl.setStyle("display", "block");
41086 this.bodyEl.setStyle("zoom", "1");
41087 //this.hideAction();
41089 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41091 this.el = Roo.get(els.el);
41092 this.inner = Roo.get(els.inner, true);
41093 this.textEl = Roo.bootstrap.version == 4 ?
41094 this.el : Roo.get(this.el.dom.firstChild, true);
41096 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41097 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41100 // this.el.on("mousedown", this.onTabMouseDown, this);
41101 this.el.on("click", this.onTabClick, this);
41103 if(config.closable){
41104 var c = Roo.get(els.close, true);
41105 c.dom.title = this.closeText;
41106 c.addClassOnOver("close-over");
41107 c.on("click", this.closeClick, this);
41113 * Fires when this tab becomes the active tab.
41114 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41115 * @param {Roo.TabPanelItem} this
41119 * @event beforeclose
41120 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41121 * @param {Roo.TabPanelItem} this
41122 * @param {Object} e Set cancel to true on this object to cancel the close.
41124 "beforeclose": true,
41127 * Fires when this tab is closed.
41128 * @param {Roo.TabPanelItem} this
41132 * @event deactivate
41133 * Fires when this tab is no longer the active tab.
41134 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41135 * @param {Roo.TabPanelItem} this
41137 "deactivate" : true
41139 this.hidden = false;
41141 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41144 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41146 purgeListeners : function(){
41147 Roo.util.Observable.prototype.purgeListeners.call(this);
41148 this.el.removeAllListeners();
41151 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41154 this.status_node.addClass("active");
41157 this.tabPanel.stripWrap.repaint();
41159 this.fireEvent("activate", this.tabPanel, this);
41163 * Returns true if this tab is the active tab.
41164 * @return {Boolean}
41166 isActive : function(){
41167 return this.tabPanel.getActiveTab() == this;
41171 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41174 this.status_node.removeClass("active");
41176 this.fireEvent("deactivate", this.tabPanel, this);
41179 hideAction : function(){
41180 this.bodyEl.hide();
41181 this.bodyEl.setStyle("position", "absolute");
41182 this.bodyEl.setLeft("-20000px");
41183 this.bodyEl.setTop("-20000px");
41186 showAction : function(){
41187 this.bodyEl.setStyle("position", "relative");
41188 this.bodyEl.setTop("");
41189 this.bodyEl.setLeft("");
41190 this.bodyEl.show();
41194 * Set the tooltip for the tab.
41195 * @param {String} tooltip The tab's tooltip
41197 setTooltip : function(text){
41198 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41199 this.textEl.dom.qtip = text;
41200 this.textEl.dom.removeAttribute('title');
41202 this.textEl.dom.title = text;
41206 onTabClick : function(e){
41207 e.preventDefault();
41208 this.tabPanel.activate(this.id);
41211 onTabMouseDown : function(e){
41212 e.preventDefault();
41213 this.tabPanel.activate(this.id);
41216 getWidth : function(){
41217 return this.inner.getWidth();
41220 setWidth : function(width){
41221 var iwidth = width - this.linode.getPadding("lr");
41222 this.inner.setWidth(iwidth);
41223 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41224 this.linode.setWidth(width);
41228 * Show or hide the tab
41229 * @param {Boolean} hidden True to hide or false to show.
41231 setHidden : function(hidden){
41232 this.hidden = hidden;
41233 this.linode.setStyle("display", hidden ? "none" : "");
41237 * Returns true if this tab is "hidden"
41238 * @return {Boolean}
41240 isHidden : function(){
41241 return this.hidden;
41245 * Returns the text for this tab
41248 getText : function(){
41252 autoSize : function(){
41253 //this.el.beginMeasure();
41254 this.textEl.setWidth(1);
41256 * #2804 [new] Tabs in Roojs
41257 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41259 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41260 //this.el.endMeasure();
41264 * Sets the text for the tab (Note: this also sets the tooltip text)
41265 * @param {String} text The tab's text and tooltip
41267 setText : function(text){
41269 this.textEl.update(text);
41270 this.setTooltip(text);
41271 //if(!this.tabPanel.resizeTabs){
41272 // this.autoSize();
41276 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41278 activate : function(){
41279 this.tabPanel.activate(this.id);
41283 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41285 disable : function(){
41286 if(this.tabPanel.active != this){
41287 this.disabled = true;
41288 this.status_node.addClass("disabled");
41293 * Enables this TabPanelItem if it was previously disabled.
41295 enable : function(){
41296 this.disabled = false;
41297 this.status_node.removeClass("disabled");
41301 * Sets the content for this TabPanelItem.
41302 * @param {String} content The content
41303 * @param {Boolean} loadScripts true to look for and load scripts
41305 setContent : function(content, loadScripts){
41306 this.bodyEl.update(content, loadScripts);
41310 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41311 * @return {Roo.UpdateManager} The UpdateManager
41313 getUpdateManager : function(){
41314 return this.bodyEl.getUpdateManager();
41318 * Set a URL to be used to load the content for this TabPanelItem.
41319 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41320 * @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)
41321 * @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)
41322 * @return {Roo.UpdateManager} The UpdateManager
41324 setUrl : function(url, params, loadOnce){
41325 if(this.refreshDelegate){
41326 this.un('activate', this.refreshDelegate);
41328 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41329 this.on("activate", this.refreshDelegate);
41330 return this.bodyEl.getUpdateManager();
41334 _handleRefresh : function(url, params, loadOnce){
41335 if(!loadOnce || !this.loaded){
41336 var updater = this.bodyEl.getUpdateManager();
41337 updater.update(url, params, this._setLoaded.createDelegate(this));
41342 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41343 * Will fail silently if the setUrl method has not been called.
41344 * This does not activate the panel, just updates its content.
41346 refresh : function(){
41347 if(this.refreshDelegate){
41348 this.loaded = false;
41349 this.refreshDelegate();
41354 _setLoaded : function(){
41355 this.loaded = true;
41359 closeClick : function(e){
41362 this.fireEvent("beforeclose", this, o);
41363 if(o.cancel !== true){
41364 this.tabPanel.removeTab(this.id);
41368 * The text displayed in the tooltip for the close icon.
41371 closeText : "Close this tab"
41374 * This script refer to:
41375 * Title: International Telephone Input
41376 * Author: Jack O'Connor
41377 * Code version: v12.1.12
41378 * Availability: https://github.com/jackocnr/intl-tel-input.git
41381 Roo.bootstrap.PhoneInputData = function() {
41384 "Afghanistan (افغانستان)",
41389 "Albania (Shqipëri)",
41394 "Algeria (الجزائر)",
41419 "Antigua and Barbuda",
41429 "Armenia (Հայաստան)",
41445 "Austria (Österreich)",
41450 "Azerbaijan (Azərbaycan)",
41460 "Bahrain (البحرين)",
41465 "Bangladesh (বাংলাদেশ)",
41475 "Belarus (Беларусь)",
41480 "Belgium (België)",
41510 "Bosnia and Herzegovina (Босна и Херцеговина)",
41525 "British Indian Ocean Territory",
41530 "British Virgin Islands",
41540 "Bulgaria (България)",
41550 "Burundi (Uburundi)",
41555 "Cambodia (កម្ពុជា)",
41560 "Cameroon (Cameroun)",
41569 ["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"]
41572 "Cape Verde (Kabu Verdi)",
41577 "Caribbean Netherlands",
41588 "Central African Republic (République centrafricaine)",
41608 "Christmas Island",
41614 "Cocos (Keeling) Islands",
41625 "Comoros (جزر القمر)",
41630 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41635 "Congo (Republic) (Congo-Brazzaville)",
41655 "Croatia (Hrvatska)",
41676 "Czech Republic (Česká republika)",
41681 "Denmark (Danmark)",
41696 "Dominican Republic (República Dominicana)",
41700 ["809", "829", "849"]
41718 "Equatorial Guinea (Guinea Ecuatorial)",
41738 "Falkland Islands (Islas Malvinas)",
41743 "Faroe Islands (Føroyar)",
41764 "French Guiana (Guyane française)",
41769 "French Polynesia (Polynésie française)",
41784 "Georgia (საქართველო)",
41789 "Germany (Deutschland)",
41809 "Greenland (Kalaallit Nunaat)",
41846 "Guinea-Bissau (Guiné Bissau)",
41871 "Hungary (Magyarország)",
41876 "Iceland (Ísland)",
41896 "Iraq (العراق)",
41912 "Israel (ישראל)",
41939 "Jordan (الأردن)",
41944 "Kazakhstan (Казахстан)",
41965 "Kuwait (الكويت)",
41970 "Kyrgyzstan (Кыргызстан)",
41980 "Latvia (Latvija)",
41985 "Lebanon (لبنان)",
42000 "Libya (ليبيا)",
42010 "Lithuania (Lietuva)",
42025 "Macedonia (FYROM) (Македонија)",
42030 "Madagascar (Madagasikara)",
42060 "Marshall Islands",
42070 "Mauritania (موريتانيا)",
42075 "Mauritius (Moris)",
42096 "Moldova (Republica Moldova)",
42106 "Mongolia (Монгол)",
42111 "Montenegro (Crna Gora)",
42121 "Morocco (المغرب)",
42127 "Mozambique (Moçambique)",
42132 "Myanmar (Burma) (မြန်မာ)",
42137 "Namibia (Namibië)",
42152 "Netherlands (Nederland)",
42157 "New Caledonia (Nouvelle-Calédonie)",
42192 "North Korea (조선 민주주의 인민 공화국)",
42197 "Northern Mariana Islands",
42213 "Pakistan (پاکستان)",
42223 "Palestine (فلسطين)",
42233 "Papua New Guinea",
42275 "Réunion (La Réunion)",
42281 "Romania (România)",
42297 "Saint Barthélemy",
42308 "Saint Kitts and Nevis",
42318 "Saint Martin (Saint-Martin (partie française))",
42324 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42329 "Saint Vincent and the Grenadines",
42344 "São Tomé and Príncipe (São Tomé e Príncipe)",
42349 "Saudi Arabia (المملكة العربية السعودية)",
42354 "Senegal (Sénégal)",
42384 "Slovakia (Slovensko)",
42389 "Slovenia (Slovenija)",
42399 "Somalia (Soomaaliya)",
42409 "South Korea (대한민국)",
42414 "South Sudan (جنوب السودان)",
42424 "Sri Lanka (ශ්රී ලංකාව)",
42429 "Sudan (السودان)",
42439 "Svalbard and Jan Mayen",
42450 "Sweden (Sverige)",
42455 "Switzerland (Schweiz)",
42460 "Syria (سوريا)",
42505 "Trinidad and Tobago",
42510 "Tunisia (تونس)",
42515 "Turkey (Türkiye)",
42525 "Turks and Caicos Islands",
42535 "U.S. Virgin Islands",
42545 "Ukraine (Україна)",
42550 "United Arab Emirates (الإمارات العربية المتحدة)",
42572 "Uzbekistan (Oʻzbekiston)",
42582 "Vatican City (Città del Vaticano)",
42593 "Vietnam (Việt Nam)",
42598 "Wallis and Futuna (Wallis-et-Futuna)",
42603 "Western Sahara (الصحراء الغربية)",
42609 "Yemen (اليمن)",
42633 * This script refer to:
42634 * Title: International Telephone Input
42635 * Author: Jack O'Connor
42636 * Code version: v12.1.12
42637 * Availability: https://github.com/jackocnr/intl-tel-input.git
42641 * @class Roo.bootstrap.PhoneInput
42642 * @extends Roo.bootstrap.TriggerField
42643 * An input with International dial-code selection
42645 * @cfg {String} defaultDialCode default '+852'
42646 * @cfg {Array} preferedCountries default []
42649 * Create a new PhoneInput.
42650 * @param {Object} config Configuration options
42653 Roo.bootstrap.PhoneInput = function(config) {
42654 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42657 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42659 listWidth: undefined,
42661 selectedClass: 'active',
42663 invalidClass : "has-warning",
42665 validClass: 'has-success',
42667 allowed: '0123456789',
42672 * @cfg {String} defaultDialCode The default dial code when initializing the input
42674 defaultDialCode: '+852',
42677 * @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
42679 preferedCountries: false,
42681 getAutoCreate : function()
42683 var data = Roo.bootstrap.PhoneInputData();
42684 var align = this.labelAlign || this.parentLabelAlign();
42687 this.allCountries = [];
42688 this.dialCodeMapping = [];
42690 for (var i = 0; i < data.length; i++) {
42692 this.allCountries[i] = {
42696 priority: c[3] || 0,
42697 areaCodes: c[4] || null
42699 this.dialCodeMapping[c[2]] = {
42702 priority: c[3] || 0,
42703 areaCodes: c[4] || null
42715 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42716 maxlength: this.max_length,
42717 cls : 'form-control tel-input',
42718 autocomplete: 'new-password'
42721 var hiddenInput = {
42724 cls: 'hidden-tel-input'
42728 hiddenInput.name = this.name;
42731 if (this.disabled) {
42732 input.disabled = true;
42735 var flag_container = {
42752 cls: this.hasFeedback ? 'has-feedback' : '',
42758 cls: 'dial-code-holder',
42765 cls: 'roo-select2-container input-group',
42772 if (this.fieldLabel.length) {
42775 tooltip: 'This field is required'
42781 cls: 'control-label',
42787 html: this.fieldLabel
42790 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42796 if(this.indicatorpos == 'right') {
42797 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42804 if(align == 'left') {
42812 if(this.labelWidth > 12){
42813 label.style = "width: " + this.labelWidth + 'px';
42815 if(this.labelWidth < 13 && this.labelmd == 0){
42816 this.labelmd = this.labelWidth;
42818 if(this.labellg > 0){
42819 label.cls += ' col-lg-' + this.labellg;
42820 input.cls += ' col-lg-' + (12 - this.labellg);
42822 if(this.labelmd > 0){
42823 label.cls += ' col-md-' + this.labelmd;
42824 container.cls += ' col-md-' + (12 - this.labelmd);
42826 if(this.labelsm > 0){
42827 label.cls += ' col-sm-' + this.labelsm;
42828 container.cls += ' col-sm-' + (12 - this.labelsm);
42830 if(this.labelxs > 0){
42831 label.cls += ' col-xs-' + this.labelxs;
42832 container.cls += ' col-xs-' + (12 - this.labelxs);
42842 var settings = this;
42844 ['xs','sm','md','lg'].map(function(size){
42845 if (settings[size]) {
42846 cfg.cls += ' col-' + size + '-' + settings[size];
42850 this.store = new Roo.data.Store({
42851 proxy : new Roo.data.MemoryProxy({}),
42852 reader : new Roo.data.JsonReader({
42863 'name' : 'dialCode',
42867 'name' : 'priority',
42871 'name' : 'areaCodes',
42878 if(!this.preferedCountries) {
42879 this.preferedCountries = [
42886 var p = this.preferedCountries.reverse();
42889 for (var i = 0; i < p.length; i++) {
42890 for (var j = 0; j < this.allCountries.length; j++) {
42891 if(this.allCountries[j].iso2 == p[i]) {
42892 var t = this.allCountries[j];
42893 this.allCountries.splice(j,1);
42894 this.allCountries.unshift(t);
42900 this.store.proxy.data = {
42902 data: this.allCountries
42908 initEvents : function()
42911 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42913 this.indicator = this.indicatorEl();
42914 this.flag = this.flagEl();
42915 this.dialCodeHolder = this.dialCodeHolderEl();
42917 this.trigger = this.el.select('div.flag-box',true).first();
42918 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42923 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42924 _this.list.setWidth(lw);
42927 this.list.on('mouseover', this.onViewOver, this);
42928 this.list.on('mousemove', this.onViewMove, this);
42929 this.inputEl().on("keyup", this.onKeyUp, this);
42930 this.inputEl().on("keypress", this.onKeyPress, this);
42932 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42934 this.view = new Roo.View(this.list, this.tpl, {
42935 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42938 this.view.on('click', this.onViewClick, this);
42939 this.setValue(this.defaultDialCode);
42942 onTriggerClick : function(e)
42944 Roo.log('trigger click');
42949 if(this.isExpanded()){
42951 this.hasFocus = false;
42953 this.store.load({});
42954 this.hasFocus = true;
42959 isExpanded : function()
42961 return this.list.isVisible();
42964 collapse : function()
42966 if(!this.isExpanded()){
42970 Roo.get(document).un('mousedown', this.collapseIf, this);
42971 Roo.get(document).un('mousewheel', this.collapseIf, this);
42972 this.fireEvent('collapse', this);
42976 expand : function()
42980 if(this.isExpanded() || !this.hasFocus){
42984 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42985 this.list.setWidth(lw);
42988 this.restrictHeight();
42990 Roo.get(document).on('mousedown', this.collapseIf, this);
42991 Roo.get(document).on('mousewheel', this.collapseIf, this);
42993 this.fireEvent('expand', this);
42996 restrictHeight : function()
42998 this.list.alignTo(this.inputEl(), this.listAlign);
42999 this.list.alignTo(this.inputEl(), this.listAlign);
43002 onViewOver : function(e, t)
43004 if(this.inKeyMode){
43007 var item = this.view.findItemFromChild(t);
43010 var index = this.view.indexOf(item);
43011 this.select(index, false);
43016 onViewClick : function(view, doFocus, el, e)
43018 var index = this.view.getSelectedIndexes()[0];
43020 var r = this.store.getAt(index);
43023 this.onSelect(r, index);
43025 if(doFocus !== false && !this.blockFocus){
43026 this.inputEl().focus();
43030 onViewMove : function(e, t)
43032 this.inKeyMode = false;
43035 select : function(index, scrollIntoView)
43037 this.selectedIndex = index;
43038 this.view.select(index);
43039 if(scrollIntoView !== false){
43040 var el = this.view.getNode(index);
43042 this.list.scrollChildIntoView(el, false);
43047 createList : function()
43049 this.list = Roo.get(document.body).createChild({
43051 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43052 style: 'display:none'
43055 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43058 collapseIf : function(e)
43060 var in_combo = e.within(this.el);
43061 var in_list = e.within(this.list);
43062 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43064 if (in_combo || in_list || is_list) {
43070 onSelect : function(record, index)
43072 if(this.fireEvent('beforeselect', this, record, index) !== false){
43074 this.setFlagClass(record.data.iso2);
43075 this.setDialCode(record.data.dialCode);
43076 this.hasFocus = false;
43078 this.fireEvent('select', this, record, index);
43082 flagEl : function()
43084 var flag = this.el.select('div.flag',true).first();
43091 dialCodeHolderEl : function()
43093 var d = this.el.select('input.dial-code-holder',true).first();
43100 setDialCode : function(v)
43102 this.dialCodeHolder.dom.value = '+'+v;
43105 setFlagClass : function(n)
43107 this.flag.dom.className = 'flag '+n;
43110 getValue : function()
43112 var v = this.inputEl().getValue();
43113 if(this.dialCodeHolder) {
43114 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43119 setValue : function(v)
43121 var d = this.getDialCode(v);
43123 //invalid dial code
43124 if(v.length == 0 || !d || d.length == 0) {
43126 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43127 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43133 this.setFlagClass(this.dialCodeMapping[d].iso2);
43134 this.setDialCode(d);
43135 this.inputEl().dom.value = v.replace('+'+d,'');
43136 this.hiddenEl().dom.value = this.getValue();
43141 getDialCode : function(v)
43145 if (v.length == 0) {
43146 return this.dialCodeHolder.dom.value;
43150 if (v.charAt(0) != "+") {
43153 var numericChars = "";
43154 for (var i = 1; i < v.length; i++) {
43155 var c = v.charAt(i);
43158 if (this.dialCodeMapping[numericChars]) {
43159 dialCode = v.substr(1, i);
43161 if (numericChars.length == 4) {
43171 this.setValue(this.defaultDialCode);
43175 hiddenEl : function()
43177 return this.el.select('input.hidden-tel-input',true).first();
43180 // after setting val
43181 onKeyUp : function(e){
43182 this.setValue(this.getValue());
43185 onKeyPress : function(e){
43186 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43193 * @class Roo.bootstrap.MoneyField
43194 * @extends Roo.bootstrap.ComboBox
43195 * Bootstrap MoneyField class
43198 * Create a new MoneyField.
43199 * @param {Object} config Configuration options
43202 Roo.bootstrap.MoneyField = function(config) {
43204 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43208 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43211 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43213 allowDecimals : true,
43215 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43217 decimalSeparator : ".",
43219 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43221 decimalPrecision : 0,
43223 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43225 allowNegative : true,
43227 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43231 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43233 minValue : Number.NEGATIVE_INFINITY,
43235 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43237 maxValue : Number.MAX_VALUE,
43239 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43241 minText : "The minimum value for this field is {0}",
43243 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43245 maxText : "The maximum value for this field is {0}",
43247 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43248 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43250 nanText : "{0} is not a valid number",
43252 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43256 * @cfg {String} defaults currency of the MoneyField
43257 * value should be in lkey
43259 defaultCurrency : false,
43261 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43263 thousandsDelimiter : false,
43265 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43276 getAutoCreate : function()
43278 var align = this.labelAlign || this.parentLabelAlign();
43290 cls : 'form-control roo-money-amount-input',
43291 autocomplete: 'new-password'
43294 var hiddenInput = {
43298 cls: 'hidden-number-input'
43301 if(this.max_length) {
43302 input.maxlength = this.max_length;
43306 hiddenInput.name = this.name;
43309 if (this.disabled) {
43310 input.disabled = true;
43313 var clg = 12 - this.inputlg;
43314 var cmd = 12 - this.inputmd;
43315 var csm = 12 - this.inputsm;
43316 var cxs = 12 - this.inputxs;
43320 cls : 'row roo-money-field',
43324 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43328 cls: 'roo-select2-container input-group',
43332 cls : 'form-control roo-money-currency-input',
43333 autocomplete: 'new-password',
43335 name : this.currencyName
43339 cls : 'input-group-addon',
43353 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43357 cls: this.hasFeedback ? 'has-feedback' : '',
43368 if (this.fieldLabel.length) {
43371 tooltip: 'This field is required'
43377 cls: 'control-label',
43383 html: this.fieldLabel
43386 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43392 if(this.indicatorpos == 'right') {
43393 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43400 if(align == 'left') {
43408 if(this.labelWidth > 12){
43409 label.style = "width: " + this.labelWidth + 'px';
43411 if(this.labelWidth < 13 && this.labelmd == 0){
43412 this.labelmd = this.labelWidth;
43414 if(this.labellg > 0){
43415 label.cls += ' col-lg-' + this.labellg;
43416 input.cls += ' col-lg-' + (12 - this.labellg);
43418 if(this.labelmd > 0){
43419 label.cls += ' col-md-' + this.labelmd;
43420 container.cls += ' col-md-' + (12 - this.labelmd);
43422 if(this.labelsm > 0){
43423 label.cls += ' col-sm-' + this.labelsm;
43424 container.cls += ' col-sm-' + (12 - this.labelsm);
43426 if(this.labelxs > 0){
43427 label.cls += ' col-xs-' + this.labelxs;
43428 container.cls += ' col-xs-' + (12 - this.labelxs);
43439 var settings = this;
43441 ['xs','sm','md','lg'].map(function(size){
43442 if (settings[size]) {
43443 cfg.cls += ' col-' + size + '-' + settings[size];
43450 initEvents : function()
43452 this.indicator = this.indicatorEl();
43454 this.initCurrencyEvent();
43456 this.initNumberEvent();
43459 initCurrencyEvent : function()
43462 throw "can not find store for combo";
43465 this.store = Roo.factory(this.store, Roo.data);
43466 this.store.parent = this;
43470 this.triggerEl = this.el.select('.input-group-addon', true).first();
43472 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43477 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43478 _this.list.setWidth(lw);
43481 this.list.on('mouseover', this.onViewOver, this);
43482 this.list.on('mousemove', this.onViewMove, this);
43483 this.list.on('scroll', this.onViewScroll, this);
43486 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43489 this.view = new Roo.View(this.list, this.tpl, {
43490 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43493 this.view.on('click', this.onViewClick, this);
43495 this.store.on('beforeload', this.onBeforeLoad, this);
43496 this.store.on('load', this.onLoad, this);
43497 this.store.on('loadexception', this.onLoadException, this);
43499 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43500 "up" : function(e){
43501 this.inKeyMode = true;
43505 "down" : function(e){
43506 if(!this.isExpanded()){
43507 this.onTriggerClick();
43509 this.inKeyMode = true;
43514 "enter" : function(e){
43517 if(this.fireEvent("specialkey", this, e)){
43518 this.onViewClick(false);
43524 "esc" : function(e){
43528 "tab" : function(e){
43531 if(this.fireEvent("specialkey", this, e)){
43532 this.onViewClick(false);
43540 doRelay : function(foo, bar, hname){
43541 if(hname == 'down' || this.scope.isExpanded()){
43542 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43550 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43554 initNumberEvent : function(e)
43556 this.inputEl().on("keydown" , this.fireKey, this);
43557 this.inputEl().on("focus", this.onFocus, this);
43558 this.inputEl().on("blur", this.onBlur, this);
43560 this.inputEl().relayEvent('keyup', this);
43562 if(this.indicator){
43563 this.indicator.addClass('invisible');
43566 this.originalValue = this.getValue();
43568 if(this.validationEvent == 'keyup'){
43569 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43570 this.inputEl().on('keyup', this.filterValidation, this);
43572 else if(this.validationEvent !== false){
43573 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43576 if(this.selectOnFocus){
43577 this.on("focus", this.preFocus, this);
43580 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43581 this.inputEl().on("keypress", this.filterKeys, this);
43583 this.inputEl().relayEvent('keypress', this);
43586 var allowed = "0123456789";
43588 if(this.allowDecimals){
43589 allowed += this.decimalSeparator;
43592 if(this.allowNegative){
43596 if(this.thousandsDelimiter) {
43600 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43602 var keyPress = function(e){
43604 var k = e.getKey();
43606 var c = e.getCharCode();
43609 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43610 allowed.indexOf(String.fromCharCode(c)) === -1
43616 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43620 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43625 this.inputEl().on("keypress", keyPress, this);
43629 onTriggerClick : function(e)
43636 this.loadNext = false;
43638 if(this.isExpanded()){
43643 this.hasFocus = true;
43645 if(this.triggerAction == 'all') {
43646 this.doQuery(this.allQuery, true);
43650 this.doQuery(this.getRawValue());
43653 getCurrency : function()
43655 var v = this.currencyEl().getValue();
43660 restrictHeight : function()
43662 this.list.alignTo(this.currencyEl(), this.listAlign);
43663 this.list.alignTo(this.currencyEl(), this.listAlign);
43666 onViewClick : function(view, doFocus, el, e)
43668 var index = this.view.getSelectedIndexes()[0];
43670 var r = this.store.getAt(index);
43673 this.onSelect(r, index);
43677 onSelect : function(record, index){
43679 if(this.fireEvent('beforeselect', this, record, index) !== false){
43681 this.setFromCurrencyData(index > -1 ? record.data : false);
43685 this.fireEvent('select', this, record, index);
43689 setFromCurrencyData : function(o)
43693 this.lastCurrency = o;
43695 if (this.currencyField) {
43696 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43698 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43701 this.lastSelectionText = currency;
43703 //setting default currency
43704 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43705 this.setCurrency(this.defaultCurrency);
43709 this.setCurrency(currency);
43712 setFromData : function(o)
43716 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43718 this.setFromCurrencyData(c);
43723 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43725 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43728 this.setValue(value);
43732 setCurrency : function(v)
43734 this.currencyValue = v;
43737 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43742 setValue : function(v)
43744 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43750 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43752 this.inputEl().dom.value = (v == '') ? '' :
43753 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43755 if(!this.allowZero && v === '0') {
43756 this.hiddenEl().dom.value = '';
43757 this.inputEl().dom.value = '';
43764 getRawValue : function()
43766 var v = this.inputEl().getValue();
43771 getValue : function()
43773 return this.fixPrecision(this.parseValue(this.getRawValue()));
43776 parseValue : function(value)
43778 if(this.thousandsDelimiter) {
43780 r = new RegExp(",", "g");
43781 value = value.replace(r, "");
43784 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43785 return isNaN(value) ? '' : value;
43789 fixPrecision : function(value)
43791 if(this.thousandsDelimiter) {
43793 r = new RegExp(",", "g");
43794 value = value.replace(r, "");
43797 var nan = isNaN(value);
43799 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43800 return nan ? '' : value;
43802 return parseFloat(value).toFixed(this.decimalPrecision);
43805 decimalPrecisionFcn : function(v)
43807 return Math.floor(v);
43810 validateValue : function(value)
43812 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43816 var num = this.parseValue(value);
43819 this.markInvalid(String.format(this.nanText, value));
43823 if(num < this.minValue){
43824 this.markInvalid(String.format(this.minText, this.minValue));
43828 if(num > this.maxValue){
43829 this.markInvalid(String.format(this.maxText, this.maxValue));
43836 validate : function()
43838 if(this.disabled || this.allowBlank){
43843 var currency = this.getCurrency();
43845 if(this.validateValue(this.getRawValue()) && currency.length){
43850 this.markInvalid();
43854 getName: function()
43859 beforeBlur : function()
43865 var v = this.parseValue(this.getRawValue());
43872 onBlur : function()
43876 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43877 //this.el.removeClass(this.focusClass);
43880 this.hasFocus = false;
43882 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43886 var v = this.getValue();
43888 if(String(v) !== String(this.startValue)){
43889 this.fireEvent('change', this, v, this.startValue);
43892 this.fireEvent("blur", this);
43895 inputEl : function()
43897 return this.el.select('.roo-money-amount-input', true).first();
43900 currencyEl : function()
43902 return this.el.select('.roo-money-currency-input', true).first();
43905 hiddenEl : function()
43907 return this.el.select('input.hidden-number-input',true).first();
43911 * @class Roo.bootstrap.BezierSignature
43912 * @extends Roo.bootstrap.Component
43913 * Bootstrap BezierSignature class
43914 * This script refer to:
43915 * Title: Signature Pad
43917 * Availability: https://github.com/szimek/signature_pad
43920 * Create a new BezierSignature
43921 * @param {Object} config The config object
43924 Roo.bootstrap.BezierSignature = function(config){
43925 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43931 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43938 mouse_btn_down: true,
43941 * @cfg {int} canvas height
43943 canvas_height: '200px',
43946 * @cfg {float|function} Radius of a single dot.
43951 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43956 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43961 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43966 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43971 * @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.
43973 bg_color: 'rgba(0, 0, 0, 0)',
43976 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43978 dot_color: 'black',
43981 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43983 velocity_filter_weight: 0.7,
43986 * @cfg {function} Callback when stroke begin.
43991 * @cfg {function} Callback when stroke end.
43995 getAutoCreate : function()
43997 var cls = 'roo-signature column';
44000 cls += ' ' + this.cls;
44010 for(var i = 0; i < col_sizes.length; i++) {
44011 if(this[col_sizes[i]]) {
44012 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44022 cls: 'roo-signature-body',
44026 cls: 'roo-signature-body-canvas',
44027 height: this.canvas_height,
44028 width: this.canvas_width
44035 style: 'display: none'
44043 initEvents: function()
44045 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44047 var canvas = this.canvasEl();
44049 // mouse && touch event swapping...
44050 canvas.dom.style.touchAction = 'none';
44051 canvas.dom.style.msTouchAction = 'none';
44053 this.mouse_btn_down = false;
44054 canvas.on('mousedown', this._handleMouseDown, this);
44055 canvas.on('mousemove', this._handleMouseMove, this);
44056 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44058 if (window.PointerEvent) {
44059 canvas.on('pointerdown', this._handleMouseDown, this);
44060 canvas.on('pointermove', this._handleMouseMove, this);
44061 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44064 if ('ontouchstart' in window) {
44065 canvas.on('touchstart', this._handleTouchStart, this);
44066 canvas.on('touchmove', this._handleTouchMove, this);
44067 canvas.on('touchend', this._handleTouchEnd, this);
44070 Roo.EventManager.onWindowResize(this.resize, this, true);
44072 // file input event
44073 this.fileEl().on('change', this.uploadImage, this);
44080 resize: function(){
44082 var canvas = this.canvasEl().dom;
44083 var ctx = this.canvasElCtx();
44084 var img_data = false;
44086 if(canvas.width > 0) {
44087 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44089 // setting canvas width will clean img data
44092 var style = window.getComputedStyle ?
44093 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44095 var padding_left = parseInt(style.paddingLeft) || 0;
44096 var padding_right = parseInt(style.paddingRight) || 0;
44098 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44101 ctx.putImageData(img_data, 0, 0);
44105 _handleMouseDown: function(e)
44107 if (e.browserEvent.which === 1) {
44108 this.mouse_btn_down = true;
44109 this.strokeBegin(e);
44113 _handleMouseMove: function (e)
44115 if (this.mouse_btn_down) {
44116 this.strokeMoveUpdate(e);
44120 _handleMouseUp: function (e)
44122 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44123 this.mouse_btn_down = false;
44128 _handleTouchStart: function (e) {
44130 e.preventDefault();
44131 if (e.browserEvent.targetTouches.length === 1) {
44132 // var touch = e.browserEvent.changedTouches[0];
44133 // this.strokeBegin(touch);
44135 this.strokeBegin(e); // assume e catching the correct xy...
44139 _handleTouchMove: function (e) {
44140 e.preventDefault();
44141 // var touch = event.targetTouches[0];
44142 // _this._strokeMoveUpdate(touch);
44143 this.strokeMoveUpdate(e);
44146 _handleTouchEnd: function (e) {
44147 var wasCanvasTouched = e.target === this.canvasEl().dom;
44148 if (wasCanvasTouched) {
44149 e.preventDefault();
44150 // var touch = event.changedTouches[0];
44151 // _this._strokeEnd(touch);
44156 reset: function () {
44157 this._lastPoints = [];
44158 this._lastVelocity = 0;
44159 this._lastWidth = (this.min_width + this.max_width) / 2;
44160 this.canvasElCtx().fillStyle = this.dot_color;
44163 strokeMoveUpdate: function(e)
44165 this.strokeUpdate(e);
44167 if (this.throttle) {
44168 this.throttleStroke(this.strokeUpdate, this.throttle);
44171 this.strokeUpdate(e);
44175 strokeBegin: function(e)
44177 var newPointGroup = {
44178 color: this.dot_color,
44182 if (typeof this.onBegin === 'function') {
44186 this.curve_data.push(newPointGroup);
44188 this.strokeUpdate(e);
44191 strokeUpdate: function(e)
44193 var rect = this.canvasEl().dom.getBoundingClientRect();
44194 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44195 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44196 var lastPoints = lastPointGroup.points;
44197 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44198 var isLastPointTooClose = lastPoint
44199 ? point.distanceTo(lastPoint) <= this.min_distance
44201 var color = lastPointGroup.color;
44202 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44203 var curve = this.addPoint(point);
44205 this.drawDot({color: color, point: point});
44208 this.drawCurve({color: color, curve: curve});
44218 strokeEnd: function(e)
44220 this.strokeUpdate(e);
44221 if (typeof this.onEnd === 'function') {
44226 addPoint: function (point) {
44227 var _lastPoints = this._lastPoints;
44228 _lastPoints.push(point);
44229 if (_lastPoints.length > 2) {
44230 if (_lastPoints.length === 3) {
44231 _lastPoints.unshift(_lastPoints[0]);
44233 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44234 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44235 _lastPoints.shift();
44241 calculateCurveWidths: function (startPoint, endPoint) {
44242 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44243 (1 - this.velocity_filter_weight) * this._lastVelocity;
44245 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44248 start: this._lastWidth
44251 this._lastVelocity = velocity;
44252 this._lastWidth = newWidth;
44256 drawDot: function (_a) {
44257 var color = _a.color, point = _a.point;
44258 var ctx = this.canvasElCtx();
44259 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44261 this.drawCurveSegment(point.x, point.y, width);
44263 ctx.fillStyle = color;
44267 drawCurve: function (_a) {
44268 var color = _a.color, curve = _a.curve;
44269 var ctx = this.canvasElCtx();
44270 var widthDelta = curve.endWidth - curve.startWidth;
44271 var drawSteps = Math.floor(curve.length()) * 2;
44273 ctx.fillStyle = color;
44274 for (var i = 0; i < drawSteps; i += 1) {
44275 var t = i / drawSteps;
44281 var x = uuu * curve.startPoint.x;
44282 x += 3 * uu * t * curve.control1.x;
44283 x += 3 * u * tt * curve.control2.x;
44284 x += ttt * curve.endPoint.x;
44285 var y = uuu * curve.startPoint.y;
44286 y += 3 * uu * t * curve.control1.y;
44287 y += 3 * u * tt * curve.control2.y;
44288 y += ttt * curve.endPoint.y;
44289 var width = curve.startWidth + ttt * widthDelta;
44290 this.drawCurveSegment(x, y, width);
44296 drawCurveSegment: function (x, y, width) {
44297 var ctx = this.canvasElCtx();
44299 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44300 this.is_empty = false;
44305 var ctx = this.canvasElCtx();
44306 var canvas = this.canvasEl().dom;
44307 ctx.fillStyle = this.bg_color;
44308 ctx.clearRect(0, 0, canvas.width, canvas.height);
44309 ctx.fillRect(0, 0, canvas.width, canvas.height);
44310 this.curve_data = [];
44312 this.is_empty = true;
44317 return this.el.select('input',true).first();
44320 canvasEl: function()
44322 return this.el.select('canvas',true).first();
44325 canvasElCtx: function()
44327 return this.el.select('canvas',true).first().dom.getContext('2d');
44330 getImage: function(type)
44332 if(this.is_empty) {
44337 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44340 drawFromImage: function(img_src)
44342 var img = new Image();
44344 img.onload = function(){
44345 this.canvasElCtx().drawImage(img, 0, 0);
44350 this.is_empty = false;
44353 selectImage: function()
44355 this.fileEl().dom.click();
44358 uploadImage: function(e)
44360 var reader = new FileReader();
44362 reader.onload = function(e){
44363 var img = new Image();
44364 img.onload = function(){
44366 this.canvasElCtx().drawImage(img, 0, 0);
44368 img.src = e.target.result;
44371 reader.readAsDataURL(e.target.files[0]);
44374 // Bezier Point Constructor
44375 Point: (function () {
44376 function Point(x, y, time) {
44379 this.time = time || Date.now();
44381 Point.prototype.distanceTo = function (start) {
44382 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44384 Point.prototype.equals = function (other) {
44385 return this.x === other.x && this.y === other.y && this.time === other.time;
44387 Point.prototype.velocityFrom = function (start) {
44388 return this.time !== start.time
44389 ? this.distanceTo(start) / (this.time - start.time)
44396 // Bezier Constructor
44397 Bezier: (function () {
44398 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44399 this.startPoint = startPoint;
44400 this.control2 = control2;
44401 this.control1 = control1;
44402 this.endPoint = endPoint;
44403 this.startWidth = startWidth;
44404 this.endWidth = endWidth;
44406 Bezier.fromPoints = function (points, widths, scope) {
44407 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44408 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44409 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44411 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44412 var dx1 = s1.x - s2.x;
44413 var dy1 = s1.y - s2.y;
44414 var dx2 = s2.x - s3.x;
44415 var dy2 = s2.y - s3.y;
44416 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44417 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44418 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44419 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44420 var dxm = m1.x - m2.x;
44421 var dym = m1.y - m2.y;
44422 var k = l2 / (l1 + l2);
44423 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44424 var tx = s2.x - cm.x;
44425 var ty = s2.y - cm.y;
44427 c1: new scope.Point(m1.x + tx, m1.y + ty),
44428 c2: new scope.Point(m2.x + tx, m2.y + ty)
44431 Bezier.prototype.length = function () {
44436 for (var i = 0; i <= steps; i += 1) {
44438 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44439 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44441 var xdiff = cx - px;
44442 var ydiff = cy - py;
44443 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44450 Bezier.prototype.point = function (t, start, c1, c2, end) {
44451 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44452 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44453 + (3.0 * c2 * (1.0 - t) * t * t)
44454 + (end * t * t * t);
44459 throttleStroke: function(fn, wait) {
44460 if (wait === void 0) { wait = 250; }
44462 var timeout = null;
44466 var later = function () {
44467 previous = Date.now();
44469 result = fn.apply(storedContext, storedArgs);
44471 storedContext = null;
44475 return function wrapper() {
44477 for (var _i = 0; _i < arguments.length; _i++) {
44478 args[_i] = arguments[_i];
44480 var now = Date.now();
44481 var remaining = wait - (now - previous);
44482 storedContext = this;
44484 if (remaining <= 0 || remaining > wait) {
44486 clearTimeout(timeout);
44490 result = fn.apply(storedContext, storedArgs);
44492 storedContext = null;
44496 else if (!timeout) {
44497 timeout = window.setTimeout(later, remaining);