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);
12756 this.updateInput();
12759 removeCard : function(id)
12762 var card = this.fileCollection.get(id);
12763 card.data.is_deleted = 1;
12764 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12765 this.fileCollection.remove(card);
12766 //this.items = this.items.filter(function(e) { return e != card });
12767 // dont' really need ot update items.
12768 card.el.dom.parentNode.removeChild(card.el.dom);
12773 this.fileCollection.each(function(card) {
12774 card.el.dom.parentNode.removeChild(card.el.dom);
12776 this.fileCollection.clear();
12777 this.updateInput();
12780 updateInput : function()
12784 var dom = this.inputEl().dom;
12785 var fc = this.fileCollection;
12786 var next = function() {
12787 if (i >= fc.length) {
12788 dom.value = JSON.stringify(data);
12791 var reader = new FileReader();
12792 reader.onloadend = function(evt) {
12794 var ee = Roo.apply({}, fc[i]);
12795 ee.src = evt.target.result;
12799 reader.readAsDataURL(fc[i].src);
12810 Roo.bootstrap.CardUploader.ID = -1;/*
12812 * Ext JS Library 1.1.1
12813 * Copyright(c) 2006-2007, Ext JS, LLC.
12815 * Originally Released Under LGPL - original licence link has changed is not relivant.
12818 * <script type="text/javascript">
12823 * @class Roo.data.SortTypes
12825 * Defines the default sorting (casting?) comparison functions used when sorting data.
12827 Roo.data.SortTypes = {
12829 * Default sort that does nothing
12830 * @param {Mixed} s The value being converted
12831 * @return {Mixed} The comparison value
12833 none : function(s){
12838 * The regular expression used to strip tags
12842 stripTagsRE : /<\/?[^>]+>/gi,
12845 * Strips all HTML tags to sort on text only
12846 * @param {Mixed} s The value being converted
12847 * @return {String} The comparison value
12849 asText : function(s){
12850 return String(s).replace(this.stripTagsRE, "");
12854 * Strips all HTML tags to sort on text only - Case insensitive
12855 * @param {Mixed} s The value being converted
12856 * @return {String} The comparison value
12858 asUCText : function(s){
12859 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12863 * Case insensitive string
12864 * @param {Mixed} s The value being converted
12865 * @return {String} The comparison value
12867 asUCString : function(s) {
12868 return String(s).toUpperCase();
12873 * @param {Mixed} s The value being converted
12874 * @return {Number} The comparison value
12876 asDate : function(s) {
12880 if(s instanceof Date){
12881 return s.getTime();
12883 return Date.parse(String(s));
12888 * @param {Mixed} s The value being converted
12889 * @return {Float} The comparison value
12891 asFloat : function(s) {
12892 var val = parseFloat(String(s).replace(/,/g, ""));
12901 * @param {Mixed} s The value being converted
12902 * @return {Number} The comparison value
12904 asInt : function(s) {
12905 var val = parseInt(String(s).replace(/,/g, ""));
12913 * Ext JS Library 1.1.1
12914 * Copyright(c) 2006-2007, Ext JS, LLC.
12916 * Originally Released Under LGPL - original licence link has changed is not relivant.
12919 * <script type="text/javascript">
12923 * @class Roo.data.Record
12924 * Instances of this class encapsulate both record <em>definition</em> information, and record
12925 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12926 * to access Records cached in an {@link Roo.data.Store} object.<br>
12928 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12929 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12932 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12934 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12935 * {@link #create}. The parameters are the same.
12936 * @param {Array} data An associative Array of data values keyed by the field name.
12937 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12938 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12939 * not specified an integer id is generated.
12941 Roo.data.Record = function(data, id){
12942 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12947 * Generate a constructor for a specific record layout.
12948 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12949 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12950 * Each field definition object may contain the following properties: <ul>
12951 * <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,
12952 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12953 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12954 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12955 * is being used, then this is a string containing the javascript expression to reference the data relative to
12956 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12957 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12958 * this may be omitted.</p></li>
12959 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12960 * <ul><li>auto (Default, implies no conversion)</li>
12965 * <li>date</li></ul></p></li>
12966 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12967 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12968 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12969 * by the Reader into an object that will be stored in the Record. It is passed the
12970 * following parameters:<ul>
12971 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12973 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12975 * <br>usage:<br><pre><code>
12976 var TopicRecord = Roo.data.Record.create(
12977 {name: 'title', mapping: 'topic_title'},
12978 {name: 'author', mapping: 'username'},
12979 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12980 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12981 {name: 'lastPoster', mapping: 'user2'},
12982 {name: 'excerpt', mapping: 'post_text'}
12985 var myNewRecord = new TopicRecord({
12986 title: 'Do my job please',
12989 lastPost: new Date(),
12990 lastPoster: 'Animal',
12991 excerpt: 'No way dude!'
12993 myStore.add(myNewRecord);
12998 Roo.data.Record.create = function(o){
12999 var f = function(){
13000 f.superclass.constructor.apply(this, arguments);
13002 Roo.extend(f, Roo.data.Record);
13003 var p = f.prototype;
13004 p.fields = new Roo.util.MixedCollection(false, function(field){
13007 for(var i = 0, len = o.length; i < len; i++){
13008 p.fields.add(new Roo.data.Field(o[i]));
13010 f.getField = function(name){
13011 return p.fields.get(name);
13016 Roo.data.Record.AUTO_ID = 1000;
13017 Roo.data.Record.EDIT = 'edit';
13018 Roo.data.Record.REJECT = 'reject';
13019 Roo.data.Record.COMMIT = 'commit';
13021 Roo.data.Record.prototype = {
13023 * Readonly flag - true if this record has been modified.
13032 join : function(store){
13033 this.store = store;
13037 * Set the named field to the specified value.
13038 * @param {String} name The name of the field to set.
13039 * @param {Object} value The value to set the field to.
13041 set : function(name, value){
13042 if(this.data[name] == value){
13046 if(!this.modified){
13047 this.modified = {};
13049 if(typeof this.modified[name] == 'undefined'){
13050 this.modified[name] = this.data[name];
13052 this.data[name] = value;
13053 if(!this.editing && this.store){
13054 this.store.afterEdit(this);
13059 * Get the value of the named field.
13060 * @param {String} name The name of the field to get the value of.
13061 * @return {Object} The value of the field.
13063 get : function(name){
13064 return this.data[name];
13068 beginEdit : function(){
13069 this.editing = true;
13070 this.modified = {};
13074 cancelEdit : function(){
13075 this.editing = false;
13076 delete this.modified;
13080 endEdit : function(){
13081 this.editing = false;
13082 if(this.dirty && this.store){
13083 this.store.afterEdit(this);
13088 * Usually called by the {@link Roo.data.Store} which owns the Record.
13089 * Rejects all changes made to the Record since either creation, or the last commit operation.
13090 * Modified fields are reverted to their original values.
13092 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093 * of reject operations.
13095 reject : function(){
13096 var m = this.modified;
13098 if(typeof m[n] != "function"){
13099 this.data[n] = m[n];
13102 this.dirty = false;
13103 delete this.modified;
13104 this.editing = false;
13106 this.store.afterReject(this);
13111 * Usually called by the {@link Roo.data.Store} which owns the Record.
13112 * Commits all changes made to the Record since either creation, or the last commit operation.
13114 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13115 * of commit operations.
13117 commit : function(){
13118 this.dirty = false;
13119 delete this.modified;
13120 this.editing = false;
13122 this.store.afterCommit(this);
13127 hasError : function(){
13128 return this.error != null;
13132 clearError : function(){
13137 * Creates a copy of this record.
13138 * @param {String} id (optional) A new record id if you don't want to use this record's id
13141 copy : function(newId) {
13142 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13146 * Ext JS Library 1.1.1
13147 * Copyright(c) 2006-2007, Ext JS, LLC.
13149 * Originally Released Under LGPL - original licence link has changed is not relivant.
13152 * <script type="text/javascript">
13158 * @class Roo.data.Store
13159 * @extends Roo.util.Observable
13160 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13161 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13163 * 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
13164 * has no knowledge of the format of the data returned by the Proxy.<br>
13166 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13167 * instances from the data object. These records are cached and made available through accessor functions.
13169 * Creates a new Store.
13170 * @param {Object} config A config object containing the objects needed for the Store to access data,
13171 * and read the data into Records.
13173 Roo.data.Store = function(config){
13174 this.data = new Roo.util.MixedCollection(false);
13175 this.data.getKey = function(o){
13178 this.baseParams = {};
13180 this.paramNames = {
13185 "multisort" : "_multisort"
13188 if(config && config.data){
13189 this.inlineData = config.data;
13190 delete config.data;
13193 Roo.apply(this, config);
13195 if(this.reader){ // reader passed
13196 this.reader = Roo.factory(this.reader, Roo.data);
13197 this.reader.xmodule = this.xmodule || false;
13198 if(!this.recordType){
13199 this.recordType = this.reader.recordType;
13201 if(this.reader.onMetaChange){
13202 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13206 if(this.recordType){
13207 this.fields = this.recordType.prototype.fields;
13209 this.modified = [];
13213 * @event datachanged
13214 * Fires when the data cache has changed, and a widget which is using this Store
13215 * as a Record cache should refresh its view.
13216 * @param {Store} this
13218 datachanged : true,
13220 * @event metachange
13221 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13222 * @param {Store} this
13223 * @param {Object} meta The JSON metadata
13228 * Fires when Records have been added to the Store
13229 * @param {Store} this
13230 * @param {Roo.data.Record[]} records The array of Records added
13231 * @param {Number} index The index at which the record(s) were added
13236 * Fires when a Record has been removed from the Store
13237 * @param {Store} this
13238 * @param {Roo.data.Record} record The Record that was removed
13239 * @param {Number} index The index at which the record was removed
13244 * Fires when a Record has been updated
13245 * @param {Store} this
13246 * @param {Roo.data.Record} record The Record that was updated
13247 * @param {String} operation The update operation being performed. Value may be one of:
13249 Roo.data.Record.EDIT
13250 Roo.data.Record.REJECT
13251 Roo.data.Record.COMMIT
13257 * Fires when the data cache has been cleared.
13258 * @param {Store} this
13262 * @event beforeload
13263 * Fires before a request is made for a new data object. If the beforeload handler returns false
13264 * the load action will be canceled.
13265 * @param {Store} this
13266 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13270 * @event beforeloadadd
13271 * Fires after a new set of Records has been loaded.
13272 * @param {Store} this
13273 * @param {Roo.data.Record[]} records The Records that were loaded
13274 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13276 beforeloadadd : true,
13279 * Fires after a new set of Records has been loaded, before they are added to the store.
13280 * @param {Store} this
13281 * @param {Roo.data.Record[]} records The Records that were loaded
13282 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13283 * @params {Object} return from reader
13287 * @event loadexception
13288 * Fires if an exception occurs in the Proxy during loading.
13289 * Called with the signature of the Proxy's "loadexception" event.
13290 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13293 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13294 * @param {Object} load options
13295 * @param {Object} jsonData from your request (normally this contains the Exception)
13297 loadexception : true
13301 this.proxy = Roo.factory(this.proxy, Roo.data);
13302 this.proxy.xmodule = this.xmodule || false;
13303 this.relayEvents(this.proxy, ["loadexception"]);
13305 this.sortToggle = {};
13306 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13308 Roo.data.Store.superclass.constructor.call(this);
13310 if(this.inlineData){
13311 this.loadData(this.inlineData);
13312 delete this.inlineData;
13316 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13318 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13319 * without a remote query - used by combo/forms at present.
13323 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13326 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13329 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13330 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13333 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13334 * on any HTTP request
13337 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13340 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13344 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13345 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13347 remoteSort : false,
13350 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13351 * loaded or when a record is removed. (defaults to false).
13353 pruneModifiedRecords : false,
13356 lastOptions : null,
13359 * Add Records to the Store and fires the add event.
13360 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13362 add : function(records){
13363 records = [].concat(records);
13364 for(var i = 0, len = records.length; i < len; i++){
13365 records[i].join(this);
13367 var index = this.data.length;
13368 this.data.addAll(records);
13369 this.fireEvent("add", this, records, index);
13373 * Remove a Record from the Store and fires the remove event.
13374 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13376 remove : function(record){
13377 var index = this.data.indexOf(record);
13378 this.data.removeAt(index);
13380 if(this.pruneModifiedRecords){
13381 this.modified.remove(record);
13383 this.fireEvent("remove", this, record, index);
13387 * Remove all Records from the Store and fires the clear event.
13389 removeAll : function(){
13391 if(this.pruneModifiedRecords){
13392 this.modified = [];
13394 this.fireEvent("clear", this);
13398 * Inserts Records to the Store at the given index and fires the add event.
13399 * @param {Number} index The start index at which to insert the passed Records.
13400 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13402 insert : function(index, records){
13403 records = [].concat(records);
13404 for(var i = 0, len = records.length; i < len; i++){
13405 this.data.insert(index, records[i]);
13406 records[i].join(this);
13408 this.fireEvent("add", this, records, index);
13412 * Get the index within the cache of the passed Record.
13413 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13414 * @return {Number} The index of the passed Record. Returns -1 if not found.
13416 indexOf : function(record){
13417 return this.data.indexOf(record);
13421 * Get the index within the cache of the Record with the passed id.
13422 * @param {String} id The id of the Record to find.
13423 * @return {Number} The index of the Record. Returns -1 if not found.
13425 indexOfId : function(id){
13426 return this.data.indexOfKey(id);
13430 * Get the Record with the specified id.
13431 * @param {String} id The id of the Record to find.
13432 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13434 getById : function(id){
13435 return this.data.key(id);
13439 * Get the Record at the specified index.
13440 * @param {Number} index The index of the Record to find.
13441 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13443 getAt : function(index){
13444 return this.data.itemAt(index);
13448 * Returns a range of Records between specified indices.
13449 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13450 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13451 * @return {Roo.data.Record[]} An array of Records
13453 getRange : function(start, end){
13454 return this.data.getRange(start, end);
13458 storeOptions : function(o){
13459 o = Roo.apply({}, o);
13462 this.lastOptions = o;
13466 * Loads the Record cache from the configured Proxy using the configured Reader.
13468 * If using remote paging, then the first load call must specify the <em>start</em>
13469 * and <em>limit</em> properties in the options.params property to establish the initial
13470 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13472 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13473 * and this call will return before the new data has been loaded. Perform any post-processing
13474 * in a callback function, or in a "load" event handler.</strong>
13476 * @param {Object} options An object containing properties which control loading options:<ul>
13477 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13478 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13479 * passed the following arguments:<ul>
13480 * <li>r : Roo.data.Record[]</li>
13481 * <li>options: Options object from the load call</li>
13482 * <li>success: Boolean success indicator</li></ul></li>
13483 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13484 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13487 load : function(options){
13488 options = options || {};
13489 if(this.fireEvent("beforeload", this, options) !== false){
13490 this.storeOptions(options);
13491 var p = Roo.apply(options.params || {}, this.baseParams);
13492 // if meta was not loaded from remote source.. try requesting it.
13493 if (!this.reader.metaFromRemote) {
13494 p._requestMeta = 1;
13496 if(this.sortInfo && this.remoteSort){
13497 var pn = this.paramNames;
13498 p[pn["sort"]] = this.sortInfo.field;
13499 p[pn["dir"]] = this.sortInfo.direction;
13501 if (this.multiSort) {
13502 var pn = this.paramNames;
13503 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13506 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13511 * Reloads the Record cache from the configured Proxy using the configured Reader and
13512 * the options from the last load operation performed.
13513 * @param {Object} options (optional) An object containing properties which may override the options
13514 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13515 * the most recently used options are reused).
13517 reload : function(options){
13518 this.load(Roo.applyIf(options||{}, this.lastOptions));
13522 // Called as a callback by the Reader during a load operation.
13523 loadRecords : function(o, options, success){
13524 if(!o || success === false){
13525 if(success !== false){
13526 this.fireEvent("load", this, [], options, o);
13528 if(options.callback){
13529 options.callback.call(options.scope || this, [], options, false);
13533 // if data returned failure - throw an exception.
13534 if (o.success === false) {
13535 // show a message if no listener is registered.
13536 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13537 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13539 // loadmask wil be hooked into this..
13540 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13543 var r = o.records, t = o.totalRecords || r.length;
13545 this.fireEvent("beforeloadadd", this, r, options, o);
13547 if(!options || options.add !== true){
13548 if(this.pruneModifiedRecords){
13549 this.modified = [];
13551 for(var i = 0, len = r.length; i < len; i++){
13555 this.data = this.snapshot;
13556 delete this.snapshot;
13559 this.data.addAll(r);
13560 this.totalLength = t;
13562 this.fireEvent("datachanged", this);
13564 this.totalLength = Math.max(t, this.data.length+r.length);
13568 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13570 var e = new Roo.data.Record({});
13572 e.set(this.parent.displayField, this.parent.emptyTitle);
13573 e.set(this.parent.valueField, '');
13578 this.fireEvent("load", this, r, options, o);
13579 if(options.callback){
13580 options.callback.call(options.scope || this, r, options, true);
13586 * Loads data from a passed data block. A Reader which understands the format of the data
13587 * must have been configured in the constructor.
13588 * @param {Object} data The data block from which to read the Records. The format of the data expected
13589 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13590 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13592 loadData : function(o, append){
13593 var r = this.reader.readRecords(o);
13594 this.loadRecords(r, {add: append}, true);
13598 * using 'cn' the nested child reader read the child array into it's child stores.
13599 * @param {Object} rec The record with a 'children array
13601 loadDataFromChildren : function(rec)
13603 this.loadData(this.reader.toLoadData(rec));
13608 * Gets the number of cached records.
13610 * <em>If using paging, this may not be the total size of the dataset. If the data object
13611 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13612 * the data set size</em>
13614 getCount : function(){
13615 return this.data.length || 0;
13619 * Gets the total number of records in the dataset as returned by the server.
13621 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13622 * the dataset size</em>
13624 getTotalCount : function(){
13625 return this.totalLength || 0;
13629 * Returns the sort state of the Store as an object with two properties:
13631 field {String} The name of the field by which the Records are sorted
13632 direction {String} The sort order, "ASC" or "DESC"
13635 getSortState : function(){
13636 return this.sortInfo;
13640 applySort : function(){
13641 if(this.sortInfo && !this.remoteSort){
13642 var s = this.sortInfo, f = s.field;
13643 var st = this.fields.get(f).sortType;
13644 var fn = function(r1, r2){
13645 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13646 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13648 this.data.sort(s.direction, fn);
13649 if(this.snapshot && this.snapshot != this.data){
13650 this.snapshot.sort(s.direction, fn);
13656 * Sets the default sort column and order to be used by the next load operation.
13657 * @param {String} fieldName The name of the field to sort by.
13658 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13660 setDefaultSort : function(field, dir){
13661 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13665 * Sort the Records.
13666 * If remote sorting is used, the sort is performed on the server, and the cache is
13667 * reloaded. If local sorting is used, the cache is sorted internally.
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 sort : function(fieldName, dir){
13672 var f = this.fields.get(fieldName);
13674 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13676 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13677 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13682 this.sortToggle[f.name] = dir;
13683 this.sortInfo = {field: f.name, direction: dir};
13684 if(!this.remoteSort){
13686 this.fireEvent("datachanged", this);
13688 this.load(this.lastOptions);
13693 * Calls the specified function for each of the Records in the cache.
13694 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13695 * Returning <em>false</em> aborts and exits the iteration.
13696 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13698 each : function(fn, scope){
13699 this.data.each(fn, scope);
13703 * Gets all records modified since the last commit. Modified records are persisted across load operations
13704 * (e.g., during paging).
13705 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13707 getModifiedRecords : function(){
13708 return this.modified;
13712 createFilterFn : function(property, value, anyMatch){
13713 if(!value.exec){ // not a regex
13714 value = String(value);
13715 if(value.length == 0){
13718 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13720 return function(r){
13721 return value.test(r.data[property]);
13726 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13727 * @param {String} property A field on your records
13728 * @param {Number} start The record index to start at (defaults to 0)
13729 * @param {Number} end The last record index to include (defaults to length - 1)
13730 * @return {Number} The sum
13732 sum : function(property, start, end){
13733 var rs = this.data.items, v = 0;
13734 start = start || 0;
13735 end = (end || end === 0) ? end : rs.length-1;
13737 for(var i = start; i <= end; i++){
13738 v += (rs[i].data[property] || 0);
13744 * Filter the records by a specified property.
13745 * @param {String} field A field on your records
13746 * @param {String/RegExp} value Either a string that the field
13747 * should start with or a RegExp to test against the field
13748 * @param {Boolean} anyMatch True to match any part not just the beginning
13750 filter : function(property, value, anyMatch){
13751 var fn = this.createFilterFn(property, value, anyMatch);
13752 return fn ? this.filterBy(fn) : this.clearFilter();
13756 * Filter by a function. The specified function will be called with each
13757 * record in this data source. If the function returns true the record is included,
13758 * otherwise it is filtered.
13759 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13760 * @param {Object} scope (optional) The scope of the function (defaults to this)
13762 filterBy : function(fn, scope){
13763 this.snapshot = this.snapshot || this.data;
13764 this.data = this.queryBy(fn, scope||this);
13765 this.fireEvent("datachanged", this);
13769 * Query the records by a specified property.
13770 * @param {String} field A field on your records
13771 * @param {String/RegExp} value Either a string that the field
13772 * should start with or a RegExp to test against the field
13773 * @param {Boolean} anyMatch True to match any part not just the beginning
13774 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13776 query : function(property, value, anyMatch){
13777 var fn = this.createFilterFn(property, value, anyMatch);
13778 return fn ? this.queryBy(fn) : this.data.clone();
13782 * Query by a function. The specified function will be called with each
13783 * record in this data source. If the function returns true the record is included
13785 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13786 * @param {Object} scope (optional) The scope of the function (defaults to this)
13787 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13789 queryBy : function(fn, scope){
13790 var data = this.snapshot || this.data;
13791 return data.filterBy(fn, scope||this);
13795 * Collects unique values for a particular dataIndex from this store.
13796 * @param {String} dataIndex The property to collect
13797 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13798 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13799 * @return {Array} An array of the unique values
13801 collect : function(dataIndex, allowNull, bypassFilter){
13802 var d = (bypassFilter === true && this.snapshot) ?
13803 this.snapshot.items : this.data.items;
13804 var v, sv, r = [], l = {};
13805 for(var i = 0, len = d.length; i < len; i++){
13806 v = d[i].data[dataIndex];
13808 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13817 * Revert to a view of the Record cache with no filtering applied.
13818 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13820 clearFilter : function(suppressEvent){
13821 if(this.snapshot && this.snapshot != this.data){
13822 this.data = this.snapshot;
13823 delete this.snapshot;
13824 if(suppressEvent !== true){
13825 this.fireEvent("datachanged", this);
13831 afterEdit : function(record){
13832 if(this.modified.indexOf(record) == -1){
13833 this.modified.push(record);
13835 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13839 afterReject : function(record){
13840 this.modified.remove(record);
13841 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13845 afterCommit : function(record){
13846 this.modified.remove(record);
13847 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13851 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13852 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13854 commitChanges : function(){
13855 var m = this.modified.slice(0);
13856 this.modified = [];
13857 for(var i = 0, len = m.length; i < len; i++){
13863 * Cancel outstanding changes on all changed records.
13865 rejectChanges : function(){
13866 var m = this.modified.slice(0);
13867 this.modified = [];
13868 for(var i = 0, len = m.length; i < len; i++){
13873 onMetaChange : function(meta, rtype, o){
13874 this.recordType = rtype;
13875 this.fields = rtype.prototype.fields;
13876 delete this.snapshot;
13877 this.sortInfo = meta.sortInfo || this.sortInfo;
13878 this.modified = [];
13879 this.fireEvent('metachange', this, this.reader.meta);
13882 moveIndex : function(data, type)
13884 var index = this.indexOf(data);
13886 var newIndex = index + type;
13890 this.insert(newIndex, data);
13895 * Ext JS Library 1.1.1
13896 * Copyright(c) 2006-2007, Ext JS, LLC.
13898 * Originally Released Under LGPL - original licence link has changed is not relivant.
13901 * <script type="text/javascript">
13905 * @class Roo.data.SimpleStore
13906 * @extends Roo.data.Store
13907 * Small helper class to make creating Stores from Array data easier.
13908 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13909 * @cfg {Array} fields An array of field definition objects, or field name strings.
13910 * @cfg {Object} an existing reader (eg. copied from another store)
13911 * @cfg {Array} data The multi-dimensional array of data
13913 * @param {Object} config
13915 Roo.data.SimpleStore = function(config)
13917 Roo.data.SimpleStore.superclass.constructor.call(this, {
13919 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13922 Roo.data.Record.create(config.fields)
13924 proxy : new Roo.data.MemoryProxy(config.data)
13928 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13930 * Ext JS Library 1.1.1
13931 * Copyright(c) 2006-2007, Ext JS, LLC.
13933 * Originally Released Under LGPL - original licence link has changed is not relivant.
13936 * <script type="text/javascript">
13941 * @extends Roo.data.Store
13942 * @class Roo.data.JsonStore
13943 * Small helper class to make creating Stores for JSON data easier. <br/>
13945 var store = new Roo.data.JsonStore({
13946 url: 'get-images.php',
13948 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13951 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13952 * JsonReader and HttpProxy (unless inline data is provided).</b>
13953 * @cfg {Array} fields An array of field definition objects, or field name strings.
13955 * @param {Object} config
13957 Roo.data.JsonStore = function(c){
13958 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13959 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13960 reader: new Roo.data.JsonReader(c, c.fields)
13963 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13965 * Ext JS Library 1.1.1
13966 * Copyright(c) 2006-2007, Ext JS, LLC.
13968 * Originally Released Under LGPL - original licence link has changed is not relivant.
13971 * <script type="text/javascript">
13975 Roo.data.Field = function(config){
13976 if(typeof config == "string"){
13977 config = {name: config};
13979 Roo.apply(this, config);
13982 this.type = "auto";
13985 var st = Roo.data.SortTypes;
13986 // named sortTypes are supported, here we look them up
13987 if(typeof this.sortType == "string"){
13988 this.sortType = st[this.sortType];
13991 // set default sortType for strings and dates
13992 if(!this.sortType){
13995 this.sortType = st.asUCString;
13998 this.sortType = st.asDate;
14001 this.sortType = st.none;
14006 var stripRe = /[\$,%]/g;
14008 // prebuilt conversion function for this field, instead of
14009 // switching every time we're reading a value
14011 var cv, dateFormat = this.dateFormat;
14016 cv = function(v){ return v; };
14019 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14023 return v !== undefined && v !== null && v !== '' ?
14024 parseInt(String(v).replace(stripRe, ""), 10) : '';
14029 return v !== undefined && v !== null && v !== '' ?
14030 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14035 cv = function(v){ return v === true || v === "true" || v == 1; };
14042 if(v instanceof Date){
14046 if(dateFormat == "timestamp"){
14047 return new Date(v*1000);
14049 return Date.parseDate(v, dateFormat);
14051 var parsed = Date.parse(v);
14052 return parsed ? new Date(parsed) : null;
14061 Roo.data.Field.prototype = {
14069 * Ext JS Library 1.1.1
14070 * Copyright(c) 2006-2007, Ext JS, LLC.
14072 * Originally Released Under LGPL - original licence link has changed is not relivant.
14075 * <script type="text/javascript">
14078 // Base class for reading structured data from a data source. This class is intended to be
14079 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14082 * @class Roo.data.DataReader
14083 * Base class for reading structured data from a data source. This class is intended to be
14084 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14087 Roo.data.DataReader = function(meta, recordType){
14091 this.recordType = recordType instanceof Array ?
14092 Roo.data.Record.create(recordType) : recordType;
14095 Roo.data.DataReader.prototype = {
14098 readerType : 'Data',
14100 * Create an empty record
14101 * @param {Object} data (optional) - overlay some values
14102 * @return {Roo.data.Record} record created.
14104 newRow : function(d) {
14106 this.recordType.prototype.fields.each(function(c) {
14108 case 'int' : da[c.name] = 0; break;
14109 case 'date' : da[c.name] = new Date(); break;
14110 case 'float' : da[c.name] = 0.0; break;
14111 case 'boolean' : da[c.name] = false; break;
14112 default : da[c.name] = ""; break;
14116 return new this.recordType(Roo.apply(da, d));
14122 * Ext JS Library 1.1.1
14123 * Copyright(c) 2006-2007, Ext JS, LLC.
14125 * Originally Released Under LGPL - original licence link has changed is not relivant.
14128 * <script type="text/javascript">
14132 * @class Roo.data.DataProxy
14133 * @extends Roo.data.Observable
14134 * This class is an abstract base class for implementations which provide retrieval of
14135 * unformatted data objects.<br>
14137 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14138 * (of the appropriate type which knows how to parse the data object) to provide a block of
14139 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14141 * Custom implementations must implement the load method as described in
14142 * {@link Roo.data.HttpProxy#load}.
14144 Roo.data.DataProxy = function(){
14147 * @event beforeload
14148 * Fires before a network request is made to retrieve a data object.
14149 * @param {Object} This DataProxy object.
14150 * @param {Object} params The params parameter to the load function.
14155 * Fires before the load method's callback is called.
14156 * @param {Object} This DataProxy object.
14157 * @param {Object} o The data object.
14158 * @param {Object} arg The callback argument object passed to the load function.
14162 * @event loadexception
14163 * Fires if an Exception occurs during data retrieval.
14164 * @param {Object} This DataProxy object.
14165 * @param {Object} o The data object.
14166 * @param {Object} arg The callback argument object passed to the load function.
14167 * @param {Object} e The Exception.
14169 loadexception : true
14171 Roo.data.DataProxy.superclass.constructor.call(this);
14174 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14177 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14181 * Ext JS Library 1.1.1
14182 * Copyright(c) 2006-2007, Ext JS, LLC.
14184 * Originally Released Under LGPL - original licence link has changed is not relivant.
14187 * <script type="text/javascript">
14190 * @class Roo.data.MemoryProxy
14191 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14192 * to the Reader when its load method is called.
14194 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14196 Roo.data.MemoryProxy = function(data){
14200 Roo.data.MemoryProxy.superclass.constructor.call(this);
14204 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14207 * Load data from the requested source (in this case an in-memory
14208 * data object passed to the constructor), read the data object into
14209 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14210 * process that block using the passed callback.
14211 * @param {Object} params This parameter is not used by the MemoryProxy class.
14212 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14213 * object into a block of Roo.data.Records.
14214 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14215 * The function must be passed <ul>
14216 * <li>The Record block object</li>
14217 * <li>The "arg" argument from the load function</li>
14218 * <li>A boolean success indicator</li>
14220 * @param {Object} scope The scope in which to call the callback
14221 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14223 load : function(params, reader, callback, scope, arg){
14224 params = params || {};
14227 result = reader.readRecords(params.data ? params.data :this.data);
14229 this.fireEvent("loadexception", this, arg, null, e);
14230 callback.call(scope, null, arg, false);
14233 callback.call(scope, result, arg, true);
14237 update : function(params, records){
14242 * Ext JS Library 1.1.1
14243 * Copyright(c) 2006-2007, Ext JS, LLC.
14245 * Originally Released Under LGPL - original licence link has changed is not relivant.
14248 * <script type="text/javascript">
14251 * @class Roo.data.HttpProxy
14252 * @extends Roo.data.DataProxy
14253 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14254 * configured to reference a certain URL.<br><br>
14256 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14257 * from which the running page was served.<br><br>
14259 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14261 * Be aware that to enable the browser to parse an XML document, the server must set
14262 * the Content-Type header in the HTTP response to "text/xml".
14264 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14265 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14266 * will be used to make the request.
14268 Roo.data.HttpProxy = function(conn){
14269 Roo.data.HttpProxy.superclass.constructor.call(this);
14270 // is conn a conn config or a real conn?
14272 this.useAjax = !conn || !conn.events;
14276 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14277 // thse are take from connection...
14280 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14283 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14284 * extra parameters to each request made by this object. (defaults to undefined)
14287 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14288 * to each request made by this object. (defaults to undefined)
14291 * @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)
14294 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14297 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14303 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14307 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14308 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14309 * a finer-grained basis than the DataProxy events.
14311 getConnection : function(){
14312 return this.useAjax ? Roo.Ajax : this.conn;
14316 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14317 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14318 * process that block using the passed callback.
14319 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14320 * for the request to the remote server.
14321 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14322 * object into a block of Roo.data.Records.
14323 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14324 * The function must be passed <ul>
14325 * <li>The Record block object</li>
14326 * <li>The "arg" argument from the load function</li>
14327 * <li>A boolean success indicator</li>
14329 * @param {Object} scope The scope in which to call the callback
14330 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14332 load : function(params, reader, callback, scope, arg){
14333 if(this.fireEvent("beforeload", this, params) !== false){
14335 params : params || {},
14337 callback : callback,
14342 callback : this.loadResponse,
14346 Roo.applyIf(o, this.conn);
14347 if(this.activeRequest){
14348 Roo.Ajax.abort(this.activeRequest);
14350 this.activeRequest = Roo.Ajax.request(o);
14352 this.conn.request(o);
14355 callback.call(scope||this, null, arg, false);
14360 loadResponse : function(o, success, response){
14361 delete this.activeRequest;
14363 this.fireEvent("loadexception", this, o, response);
14364 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14369 result = o.reader.read(response);
14371 this.fireEvent("loadexception", this, o, response, e);
14372 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14376 this.fireEvent("load", this, o, o.request.arg);
14377 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14381 update : function(dataSet){
14386 updateResponse : function(dataSet){
14391 * Ext JS Library 1.1.1
14392 * Copyright(c) 2006-2007, Ext JS, LLC.
14394 * Originally Released Under LGPL - original licence link has changed is not relivant.
14397 * <script type="text/javascript">
14401 * @class Roo.data.ScriptTagProxy
14402 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14403 * other than the originating domain of the running page.<br><br>
14405 * <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
14406 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14408 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14409 * source code that is used as the source inside a <script> tag.<br><br>
14411 * In order for the browser to process the returned data, the server must wrap the data object
14412 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14413 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14414 * depending on whether the callback name was passed:
14417 boolean scriptTag = false;
14418 String cb = request.getParameter("callback");
14421 response.setContentType("text/javascript");
14423 response.setContentType("application/x-json");
14425 Writer out = response.getWriter();
14427 out.write(cb + "(");
14429 out.print(dataBlock.toJsonString());
14436 * @param {Object} config A configuration object.
14438 Roo.data.ScriptTagProxy = function(config){
14439 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14440 Roo.apply(this, config);
14441 this.head = document.getElementsByTagName("head")[0];
14444 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14446 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14448 * @cfg {String} url The URL from which to request the data object.
14451 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14455 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14456 * the server the name of the callback function set up by the load call to process the returned data object.
14457 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14458 * javascript output which calls this named function passing the data object as its only parameter.
14460 callbackParam : "callback",
14462 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14463 * name to the request.
14468 * Load data from the configured URL, read the data object into
14469 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14470 * process that block using the passed callback.
14471 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14472 * for the request to the remote server.
14473 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14474 * object into a block of Roo.data.Records.
14475 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14476 * The function must be passed <ul>
14477 * <li>The Record block object</li>
14478 * <li>The "arg" argument from the load function</li>
14479 * <li>A boolean success indicator</li>
14481 * @param {Object} scope The scope in which to call the callback
14482 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14484 load : function(params, reader, callback, scope, arg){
14485 if(this.fireEvent("beforeload", this, params) !== false){
14487 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14489 var url = this.url;
14490 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14492 url += "&_dc=" + (new Date().getTime());
14494 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14497 cb : "stcCallback"+transId,
14498 scriptId : "stcScript"+transId,
14502 callback : callback,
14508 window[trans.cb] = function(o){
14509 conn.handleResponse(o, trans);
14512 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14514 if(this.autoAbort !== false){
14518 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14520 var script = document.createElement("script");
14521 script.setAttribute("src", url);
14522 script.setAttribute("type", "text/javascript");
14523 script.setAttribute("id", trans.scriptId);
14524 this.head.appendChild(script);
14526 this.trans = trans;
14528 callback.call(scope||this, null, arg, false);
14533 isLoading : function(){
14534 return this.trans ? true : false;
14538 * Abort the current server request.
14540 abort : function(){
14541 if(this.isLoading()){
14542 this.destroyTrans(this.trans);
14547 destroyTrans : function(trans, isLoaded){
14548 this.head.removeChild(document.getElementById(trans.scriptId));
14549 clearTimeout(trans.timeoutId);
14551 window[trans.cb] = undefined;
14553 delete window[trans.cb];
14556 // if hasn't been loaded, wait for load to remove it to prevent script error
14557 window[trans.cb] = function(){
14558 window[trans.cb] = undefined;
14560 delete window[trans.cb];
14567 handleResponse : function(o, trans){
14568 this.trans = false;
14569 this.destroyTrans(trans, true);
14572 result = trans.reader.readRecords(o);
14574 this.fireEvent("loadexception", this, o, trans.arg, e);
14575 trans.callback.call(trans.scope||window, null, trans.arg, false);
14578 this.fireEvent("load", this, o, trans.arg);
14579 trans.callback.call(trans.scope||window, result, trans.arg, true);
14583 handleFailure : function(trans){
14584 this.trans = false;
14585 this.destroyTrans(trans, false);
14586 this.fireEvent("loadexception", this, null, trans.arg);
14587 trans.callback.call(trans.scope||window, null, trans.arg, false);
14591 * Ext JS Library 1.1.1
14592 * Copyright(c) 2006-2007, Ext JS, LLC.
14594 * Originally Released Under LGPL - original licence link has changed is not relivant.
14597 * <script type="text/javascript">
14601 * @class Roo.data.JsonReader
14602 * @extends Roo.data.DataReader
14603 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14604 * based on mappings in a provided Roo.data.Record constructor.
14606 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14607 * in the reply previously.
14612 var RecordDef = Roo.data.Record.create([
14613 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14614 {name: 'occupation'} // This field will use "occupation" as the mapping.
14616 var myReader = new Roo.data.JsonReader({
14617 totalProperty: "results", // The property which contains the total dataset size (optional)
14618 root: "rows", // The property which contains an Array of row objects
14619 id: "id" // The property within each row object that provides an ID for the record (optional)
14623 * This would consume a JSON file like this:
14625 { 'results': 2, 'rows': [
14626 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14627 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14630 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14631 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14632 * paged from the remote server.
14633 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14634 * @cfg {String} root name of the property which contains the Array of row objects.
14635 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14636 * @cfg {Array} fields Array of field definition objects
14638 * Create a new JsonReader
14639 * @param {Object} meta Metadata configuration options
14640 * @param {Object} recordType Either an Array of field definition objects,
14641 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14643 Roo.data.JsonReader = function(meta, recordType){
14646 // set some defaults:
14647 Roo.applyIf(meta, {
14648 totalProperty: 'total',
14649 successProperty : 'success',
14654 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14656 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14658 readerType : 'Json',
14661 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14662 * Used by Store query builder to append _requestMeta to params.
14665 metaFromRemote : false,
14667 * This method is only used by a DataProxy which has retrieved data from a remote server.
14668 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14669 * @return {Object} data A data block which is used by an Roo.data.Store object as
14670 * a cache of Roo.data.Records.
14672 read : function(response){
14673 var json = response.responseText;
14675 var o = /* eval:var:o */ eval("("+json+")");
14677 throw {message: "JsonReader.read: Json object not found"};
14683 this.metaFromRemote = true;
14684 this.meta = o.metaData;
14685 this.recordType = Roo.data.Record.create(o.metaData.fields);
14686 this.onMetaChange(this.meta, this.recordType, o);
14688 return this.readRecords(o);
14691 // private function a store will implement
14692 onMetaChange : function(meta, recordType, o){
14699 simpleAccess: function(obj, subsc) {
14706 getJsonAccessor: function(){
14708 return function(expr) {
14710 return(re.test(expr))
14711 ? new Function("obj", "return obj." + expr)
14716 return Roo.emptyFn;
14721 * Create a data block containing Roo.data.Records from an XML document.
14722 * @param {Object} o An object which contains an Array of row objects in the property specified
14723 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14724 * which contains the total size of the dataset.
14725 * @return {Object} data A data block which is used by an Roo.data.Store object as
14726 * a cache of Roo.data.Records.
14728 readRecords : function(o){
14730 * After any data loads, the raw JSON data is available for further custom processing.
14734 var s = this.meta, Record = this.recordType,
14735 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14737 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14739 if(s.totalProperty) {
14740 this.getTotal = this.getJsonAccessor(s.totalProperty);
14742 if(s.successProperty) {
14743 this.getSuccess = this.getJsonAccessor(s.successProperty);
14745 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14747 var g = this.getJsonAccessor(s.id);
14748 this.getId = function(rec) {
14750 return (r === undefined || r === "") ? null : r;
14753 this.getId = function(){return null;};
14756 for(var jj = 0; jj < fl; jj++){
14758 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14759 this.ef[jj] = this.getJsonAccessor(map);
14763 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14764 if(s.totalProperty){
14765 var vt = parseInt(this.getTotal(o), 10);
14770 if(s.successProperty){
14771 var vs = this.getSuccess(o);
14772 if(vs === false || vs === 'false'){
14777 for(var i = 0; i < c; i++){
14780 var id = this.getId(n);
14781 for(var j = 0; j < fl; j++){
14783 var v = this.ef[j](n);
14785 Roo.log('missing convert for ' + f.name);
14789 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14791 var record = new Record(values, id);
14793 records[i] = record;
14799 totalRecords : totalRecords
14802 // used when loading children.. @see loadDataFromChildren
14803 toLoadData: function(rec)
14805 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14806 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14807 return { data : data, total : data.length };
14812 * Ext JS Library 1.1.1
14813 * Copyright(c) 2006-2007, Ext JS, LLC.
14815 * Originally Released Under LGPL - original licence link has changed is not relivant.
14818 * <script type="text/javascript">
14822 * @class Roo.data.ArrayReader
14823 * @extends Roo.data.DataReader
14824 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14825 * Each element of that Array represents a row of data fields. The
14826 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14827 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14831 var RecordDef = Roo.data.Record.create([
14832 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14833 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14835 var myReader = new Roo.data.ArrayReader({
14836 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14840 * This would consume an Array like this:
14842 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14846 * Create a new JsonReader
14847 * @param {Object} meta Metadata configuration options.
14848 * @param {Object|Array} recordType Either an Array of field definition objects
14850 * @cfg {Array} fields Array of field definition objects
14851 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14852 * as specified to {@link Roo.data.Record#create},
14853 * or an {@link Roo.data.Record} object
14856 * created using {@link Roo.data.Record#create}.
14858 Roo.data.ArrayReader = function(meta, recordType)
14860 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14863 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14866 * Create a data block containing Roo.data.Records from an XML document.
14867 * @param {Object} o An Array of row objects which represents the dataset.
14868 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14869 * a cache of Roo.data.Records.
14871 readRecords : function(o)
14873 var sid = this.meta ? this.meta.id : null;
14874 var recordType = this.recordType, fields = recordType.prototype.fields;
14877 for(var i = 0; i < root.length; i++){
14880 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14881 for(var j = 0, jlen = fields.length; j < jlen; j++){
14882 var f = fields.items[j];
14883 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14884 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14886 values[f.name] = v;
14888 var record = new recordType(values, id);
14890 records[records.length] = record;
14894 totalRecords : records.length
14897 // used when loading children.. @see loadDataFromChildren
14898 toLoadData: function(rec)
14900 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14901 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14912 * @class Roo.bootstrap.ComboBox
14913 * @extends Roo.bootstrap.TriggerField
14914 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14915 * @cfg {Boolean} append (true|false) default false
14916 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14917 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14918 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14919 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14920 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14921 * @cfg {Boolean} animate default true
14922 * @cfg {Boolean} emptyResultText only for touch device
14923 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14924 * @cfg {String} emptyTitle default ''
14925 * @cfg {Number} width fixed with? experimental
14927 * Create a new ComboBox.
14928 * @param {Object} config Configuration options
14930 Roo.bootstrap.ComboBox = function(config){
14931 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14935 * Fires when the dropdown list is expanded
14936 * @param {Roo.bootstrap.ComboBox} combo This combo box
14941 * Fires when the dropdown list is collapsed
14942 * @param {Roo.bootstrap.ComboBox} combo This combo box
14946 * @event beforeselect
14947 * Fires before a list item is selected. Return false to cancel the selection.
14948 * @param {Roo.bootstrap.ComboBox} combo This combo box
14949 * @param {Roo.data.Record} record The data record returned from the underlying store
14950 * @param {Number} index The index of the selected item in the dropdown list
14952 'beforeselect' : true,
14955 * Fires when a list item is selected
14956 * @param {Roo.bootstrap.ComboBox} combo This combo box
14957 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14958 * @param {Number} index The index of the selected item in the dropdown list
14962 * @event beforequery
14963 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14964 * The event object passed has these properties:
14965 * @param {Roo.bootstrap.ComboBox} combo This combo box
14966 * @param {String} query The query
14967 * @param {Boolean} forceAll true to force "all" query
14968 * @param {Boolean} cancel true to cancel the query
14969 * @param {Object} e The query event object
14971 'beforequery': true,
14974 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14975 * @param {Roo.bootstrap.ComboBox} combo This combo box
14980 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14981 * @param {Roo.bootstrap.ComboBox} combo This combo box
14982 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14987 * Fires when the remove value from the combobox array
14988 * @param {Roo.bootstrap.ComboBox} combo This combo box
14992 * @event afterremove
14993 * Fires when the remove value from the combobox array
14994 * @param {Roo.bootstrap.ComboBox} combo This combo box
14996 'afterremove' : true,
14998 * @event specialfilter
14999 * Fires when specialfilter
15000 * @param {Roo.bootstrap.ComboBox} combo This combo box
15002 'specialfilter' : true,
15005 * Fires when tick the element
15006 * @param {Roo.bootstrap.ComboBox} combo This combo box
15010 * @event touchviewdisplay
15011 * Fires when touch view require special display (default is using displayField)
15012 * @param {Roo.bootstrap.ComboBox} combo This combo box
15013 * @param {Object} cfg set html .
15015 'touchviewdisplay' : true
15020 this.tickItems = [];
15022 this.selectedIndex = -1;
15023 if(this.mode == 'local'){
15024 if(config.queryDelay === undefined){
15025 this.queryDelay = 10;
15027 if(config.minChars === undefined){
15033 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15036 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15037 * rendering into an Roo.Editor, defaults to false)
15040 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15041 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15044 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15047 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15048 * the dropdown list (defaults to undefined, with no header element)
15052 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15056 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15058 listWidth: undefined,
15060 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15061 * mode = 'remote' or 'text' if mode = 'local')
15063 displayField: undefined,
15066 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15067 * mode = 'remote' or 'value' if mode = 'local').
15068 * Note: use of a valueField requires the user make a selection
15069 * in order for a value to be mapped.
15071 valueField: undefined,
15073 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15078 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15079 * field's data value (defaults to the underlying DOM element's name)
15081 hiddenName: undefined,
15083 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15087 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15089 selectedClass: 'active',
15092 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15096 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15097 * anchor positions (defaults to 'tl-bl')
15099 listAlign: 'tl-bl?',
15101 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15105 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15106 * query specified by the allQuery config option (defaults to 'query')
15108 triggerAction: 'query',
15110 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15111 * (defaults to 4, does not apply if editable = false)
15115 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15116 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15120 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15121 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15125 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15126 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15130 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15131 * when editable = true (defaults to false)
15133 selectOnFocus:false,
15135 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15137 queryParam: 'query',
15139 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15140 * when mode = 'remote' (defaults to 'Loading...')
15142 loadingText: 'Loading...',
15144 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15148 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15152 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15153 * traditional select (defaults to true)
15157 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15161 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15165 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15166 * listWidth has a higher value)
15170 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15171 * allow the user to set arbitrary text into the field (defaults to false)
15173 forceSelection:false,
15175 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15176 * if typeAhead = true (defaults to 250)
15178 typeAheadDelay : 250,
15180 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15181 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15183 valueNotFoundText : undefined,
15185 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15187 blockFocus : false,
15190 * @cfg {Boolean} disableClear Disable showing of clear button.
15192 disableClear : false,
15194 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15196 alwaysQuery : false,
15199 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15204 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15206 invalidClass : "has-warning",
15209 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15211 validClass : "has-success",
15214 * @cfg {Boolean} specialFilter (true|false) special filter default false
15216 specialFilter : false,
15219 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15221 mobileTouchView : true,
15224 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15226 useNativeIOS : false,
15229 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15231 mobile_restrict_height : false,
15233 ios_options : false,
15245 btnPosition : 'right',
15246 triggerList : true,
15247 showToggleBtn : true,
15249 emptyResultText: 'Empty',
15250 triggerText : 'Select',
15254 // element that contains real text value.. (when hidden is used..)
15256 getAutoCreate : function()
15261 * Render classic select for iso
15264 if(Roo.isIOS && this.useNativeIOS){
15265 cfg = this.getAutoCreateNativeIOS();
15273 if(Roo.isTouch && this.mobileTouchView){
15274 cfg = this.getAutoCreateTouchView();
15281 if(!this.tickable){
15282 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15287 * ComboBox with tickable selections
15290 var align = this.labelAlign || this.parentLabelAlign();
15293 cls : 'form-group roo-combobox-tickable' //input-group
15296 var btn_text_select = '';
15297 var btn_text_done = '';
15298 var btn_text_cancel = '';
15300 if (this.btn_text_show) {
15301 btn_text_select = 'Select';
15302 btn_text_done = 'Done';
15303 btn_text_cancel = 'Cancel';
15308 cls : 'tickable-buttons',
15313 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15314 //html : this.triggerText
15315 html: btn_text_select
15321 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15323 html: btn_text_done
15329 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15331 html: btn_text_cancel
15337 buttons.cn.unshift({
15339 cls: 'roo-select2-search-field-input'
15345 Roo.each(buttons.cn, function(c){
15347 c.cls += ' btn-' + _this.size;
15350 if (_this.disabled) {
15357 style : 'display: contents',
15362 cls: 'form-hidden-field'
15366 cls: 'roo-select2-choices',
15370 cls: 'roo-select2-search-field',
15381 cls: 'roo-select2-container input-group roo-select2-container-multi',
15387 // cls: 'typeahead typeahead-long dropdown-menu',
15388 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15393 if(this.hasFeedback && !this.allowBlank){
15397 cls: 'glyphicon form-control-feedback'
15400 combobox.cn.push(feedback);
15407 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15408 tooltip : 'This field is required'
15410 if (Roo.bootstrap.version == 4) {
15413 style : 'display:none'
15416 if (align ==='left' && this.fieldLabel.length) {
15418 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15425 cls : 'control-label col-form-label',
15426 html : this.fieldLabel
15438 var labelCfg = cfg.cn[1];
15439 var contentCfg = cfg.cn[2];
15442 if(this.indicatorpos == 'right'){
15448 cls : 'control-label col-form-label',
15452 html : this.fieldLabel
15468 labelCfg = cfg.cn[0];
15469 contentCfg = cfg.cn[1];
15473 if(this.labelWidth > 12){
15474 labelCfg.style = "width: " + this.labelWidth + 'px';
15476 if(this.width * 1 > 0){
15477 contentCfg.style = "width: " + this.width + 'px';
15479 if(this.labelWidth < 13 && this.labelmd == 0){
15480 this.labelmd = this.labelWidth;
15483 if(this.labellg > 0){
15484 labelCfg.cls += ' col-lg-' + this.labellg;
15485 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15488 if(this.labelmd > 0){
15489 labelCfg.cls += ' col-md-' + this.labelmd;
15490 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15493 if(this.labelsm > 0){
15494 labelCfg.cls += ' col-sm-' + this.labelsm;
15495 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15498 if(this.labelxs > 0){
15499 labelCfg.cls += ' col-xs-' + this.labelxs;
15500 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15504 } else if ( this.fieldLabel.length) {
15505 // Roo.log(" label");
15510 //cls : 'input-group-addon',
15511 html : this.fieldLabel
15516 if(this.indicatorpos == 'right'){
15520 //cls : 'input-group-addon',
15521 html : this.fieldLabel
15531 // Roo.log(" no label && no align");
15538 ['xs','sm','md','lg'].map(function(size){
15539 if (settings[size]) {
15540 cfg.cls += ' col-' + size + '-' + settings[size];
15548 _initEventsCalled : false,
15551 initEvents: function()
15553 if (this._initEventsCalled) { // as we call render... prevent looping...
15556 this._initEventsCalled = true;
15559 throw "can not find store for combo";
15562 this.indicator = this.indicatorEl();
15564 this.store = Roo.factory(this.store, Roo.data);
15565 this.store.parent = this;
15567 // if we are building from html. then this element is so complex, that we can not really
15568 // use the rendered HTML.
15569 // so we have to trash and replace the previous code.
15570 if (Roo.XComponent.build_from_html) {
15571 // remove this element....
15572 var e = this.el.dom, k=0;
15573 while (e ) { e = e.previousSibling; ++k;}
15578 this.rendered = false;
15580 this.render(this.parent().getChildContainer(true), k);
15583 if(Roo.isIOS && this.useNativeIOS){
15584 this.initIOSView();
15592 if(Roo.isTouch && this.mobileTouchView){
15593 this.initTouchView();
15598 this.initTickableEvents();
15602 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15604 if(this.hiddenName){
15606 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15608 this.hiddenField.dom.value =
15609 this.hiddenValue !== undefined ? this.hiddenValue :
15610 this.value !== undefined ? this.value : '';
15612 // prevent input submission
15613 this.el.dom.removeAttribute('name');
15614 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15619 // this.el.dom.setAttribute('autocomplete', 'off');
15622 var cls = 'x-combo-list';
15624 //this.list = new Roo.Layer({
15625 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15631 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15632 _this.list.setWidth(lw);
15635 this.list.on('mouseover', this.onViewOver, this);
15636 this.list.on('mousemove', this.onViewMove, this);
15637 this.list.on('scroll', this.onViewScroll, this);
15640 this.list.swallowEvent('mousewheel');
15641 this.assetHeight = 0;
15644 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15645 this.assetHeight += this.header.getHeight();
15648 this.innerList = this.list.createChild({cls:cls+'-inner'});
15649 this.innerList.on('mouseover', this.onViewOver, this);
15650 this.innerList.on('mousemove', this.onViewMove, this);
15651 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15653 if(this.allowBlank && !this.pageSize && !this.disableClear){
15654 this.footer = this.list.createChild({cls:cls+'-ft'});
15655 this.pageTb = new Roo.Toolbar(this.footer);
15659 this.footer = this.list.createChild({cls:cls+'-ft'});
15660 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15661 {pageSize: this.pageSize});
15665 if (this.pageTb && this.allowBlank && !this.disableClear) {
15667 this.pageTb.add(new Roo.Toolbar.Fill(), {
15668 cls: 'x-btn-icon x-btn-clear',
15670 handler: function()
15673 _this.clearValue();
15674 _this.onSelect(false, -1);
15679 this.assetHeight += this.footer.getHeight();
15684 this.tpl = Roo.bootstrap.version == 4 ?
15685 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15686 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15689 this.view = new Roo.View(this.list, this.tpl, {
15690 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15692 //this.view.wrapEl.setDisplayed(false);
15693 this.view.on('click', this.onViewClick, this);
15696 this.store.on('beforeload', this.onBeforeLoad, this);
15697 this.store.on('load', this.onLoad, this);
15698 this.store.on('loadexception', this.onLoadException, this);
15700 if(this.resizable){
15701 this.resizer = new Roo.Resizable(this.list, {
15702 pinned:true, handles:'se'
15704 this.resizer.on('resize', function(r, w, h){
15705 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15706 this.listWidth = w;
15707 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15708 this.restrictHeight();
15710 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15713 if(!this.editable){
15714 this.editable = true;
15715 this.setEditable(false);
15720 if (typeof(this.events.add.listeners) != 'undefined') {
15722 this.addicon = this.wrap.createChild(
15723 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15725 this.addicon.on('click', function(e) {
15726 this.fireEvent('add', this);
15729 if (typeof(this.events.edit.listeners) != 'undefined') {
15731 this.editicon = this.wrap.createChild(
15732 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15733 if (this.addicon) {
15734 this.editicon.setStyle('margin-left', '40px');
15736 this.editicon.on('click', function(e) {
15738 // we fire even if inothing is selected..
15739 this.fireEvent('edit', this, this.lastData );
15745 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15746 "up" : function(e){
15747 this.inKeyMode = true;
15751 "down" : function(e){
15752 if(!this.isExpanded()){
15753 this.onTriggerClick();
15755 this.inKeyMode = true;
15760 "enter" : function(e){
15761 // this.onViewClick();
15765 if(this.fireEvent("specialkey", this, e)){
15766 this.onViewClick(false);
15772 "esc" : function(e){
15776 "tab" : function(e){
15779 if(this.fireEvent("specialkey", this, e)){
15780 this.onViewClick(false);
15788 doRelay : function(foo, bar, hname){
15789 if(hname == 'down' || this.scope.isExpanded()){
15790 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15799 this.queryDelay = Math.max(this.queryDelay || 10,
15800 this.mode == 'local' ? 10 : 250);
15803 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15805 if(this.typeAhead){
15806 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15808 if(this.editable !== false){
15809 this.inputEl().on("keyup", this.onKeyUp, this);
15811 if(this.forceSelection){
15812 this.inputEl().on('blur', this.doForce, this);
15816 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15817 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15821 initTickableEvents: function()
15825 if(this.hiddenName){
15827 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15829 this.hiddenField.dom.value =
15830 this.hiddenValue !== undefined ? this.hiddenValue :
15831 this.value !== undefined ? this.value : '';
15833 // prevent input submission
15834 this.el.dom.removeAttribute('name');
15835 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15840 // this.list = this.el.select('ul.dropdown-menu',true).first();
15842 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15843 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15844 if(this.triggerList){
15845 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15848 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15849 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15851 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15852 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15854 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15855 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15857 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15858 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15859 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15862 this.cancelBtn.hide();
15867 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15868 _this.list.setWidth(lw);
15871 this.list.on('mouseover', this.onViewOver, this);
15872 this.list.on('mousemove', this.onViewMove, this);
15874 this.list.on('scroll', this.onViewScroll, this);
15877 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15878 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15881 this.view = new Roo.View(this.list, this.tpl, {
15886 selectedClass: this.selectedClass
15889 //this.view.wrapEl.setDisplayed(false);
15890 this.view.on('click', this.onViewClick, this);
15894 this.store.on('beforeload', this.onBeforeLoad, this);
15895 this.store.on('load', this.onLoad, this);
15896 this.store.on('loadexception', this.onLoadException, this);
15899 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15900 "up" : function(e){
15901 this.inKeyMode = true;
15905 "down" : function(e){
15906 this.inKeyMode = true;
15910 "enter" : function(e){
15911 if(this.fireEvent("specialkey", this, e)){
15912 this.onViewClick(false);
15918 "esc" : function(e){
15919 this.onTickableFooterButtonClick(e, false, false);
15922 "tab" : function(e){
15923 this.fireEvent("specialkey", this, e);
15925 this.onTickableFooterButtonClick(e, false, false);
15932 doRelay : function(e, fn, key){
15933 if(this.scope.isExpanded()){
15934 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15943 this.queryDelay = Math.max(this.queryDelay || 10,
15944 this.mode == 'local' ? 10 : 250);
15947 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15949 if(this.typeAhead){
15950 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15953 if(this.editable !== false){
15954 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15957 this.indicator = this.indicatorEl();
15959 if(this.indicator){
15960 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15961 this.indicator.hide();
15966 onDestroy : function(){
15968 this.view.setStore(null);
15969 this.view.el.removeAllListeners();
15970 this.view.el.remove();
15971 this.view.purgeListeners();
15974 this.list.dom.innerHTML = '';
15978 this.store.un('beforeload', this.onBeforeLoad, this);
15979 this.store.un('load', this.onLoad, this);
15980 this.store.un('loadexception', this.onLoadException, this);
15982 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15986 fireKey : function(e){
15987 if(e.isNavKeyPress() && !this.list.isVisible()){
15988 this.fireEvent("specialkey", this, e);
15993 onResize: function(w, h)
15997 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15999 // if(typeof w != 'number'){
16000 // // we do not handle it!?!?
16003 // var tw = this.trigger.getWidth();
16004 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16005 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16007 // this.inputEl().setWidth( this.adjustWidth('input', x));
16009 // //this.trigger.setStyle('left', x+'px');
16011 // if(this.list && this.listWidth === undefined){
16012 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16013 // this.list.setWidth(lw);
16014 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16022 * Allow or prevent the user from directly editing the field text. If false is passed,
16023 * the user will only be able to select from the items defined in the dropdown list. This method
16024 * is the runtime equivalent of setting the 'editable' config option at config time.
16025 * @param {Boolean} value True to allow the user to directly edit the field text
16027 setEditable : function(value){
16028 if(value == this.editable){
16031 this.editable = value;
16033 this.inputEl().dom.setAttribute('readOnly', true);
16034 this.inputEl().on('mousedown', this.onTriggerClick, this);
16035 this.inputEl().addClass('x-combo-noedit');
16037 this.inputEl().dom.setAttribute('readOnly', false);
16038 this.inputEl().un('mousedown', this.onTriggerClick, this);
16039 this.inputEl().removeClass('x-combo-noedit');
16045 onBeforeLoad : function(combo,opts){
16046 if(!this.hasFocus){
16050 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16052 this.restrictHeight();
16053 this.selectedIndex = -1;
16057 onLoad : function(){
16059 this.hasQuery = false;
16061 if(!this.hasFocus){
16065 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16066 this.loading.hide();
16069 if(this.store.getCount() > 0){
16072 this.restrictHeight();
16073 if(this.lastQuery == this.allQuery){
16074 if(this.editable && !this.tickable){
16075 this.inputEl().dom.select();
16079 !this.selectByValue(this.value, true) &&
16082 !this.store.lastOptions ||
16083 typeof(this.store.lastOptions.add) == 'undefined' ||
16084 this.store.lastOptions.add != true
16087 this.select(0, true);
16090 if(this.autoFocus){
16093 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16094 this.taTask.delay(this.typeAheadDelay);
16098 this.onEmptyResults();
16104 onLoadException : function()
16106 this.hasQuery = false;
16108 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16109 this.loading.hide();
16112 if(this.tickable && this.editable){
16117 // only causes errors at present
16118 //Roo.log(this.store.reader.jsonData);
16119 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16121 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16127 onTypeAhead : function(){
16128 if(this.store.getCount() > 0){
16129 var r = this.store.getAt(0);
16130 var newValue = r.data[this.displayField];
16131 var len = newValue.length;
16132 var selStart = this.getRawValue().length;
16134 if(selStart != len){
16135 this.setRawValue(newValue);
16136 this.selectText(selStart, newValue.length);
16142 onSelect : function(record, index){
16144 if(this.fireEvent('beforeselect', this, record, index) !== false){
16146 this.setFromData(index > -1 ? record.data : false);
16149 this.fireEvent('select', this, record, index);
16154 * Returns the currently selected field value or empty string if no value is set.
16155 * @return {String} value The selected value
16157 getValue : function()
16159 if(Roo.isIOS && this.useNativeIOS){
16160 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16164 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16167 if(this.valueField){
16168 return typeof this.value != 'undefined' ? this.value : '';
16170 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16174 getRawValue : function()
16176 if(Roo.isIOS && this.useNativeIOS){
16177 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16180 var v = this.inputEl().getValue();
16186 * Clears any text/value currently set in the field
16188 clearValue : function(){
16190 if(this.hiddenField){
16191 this.hiddenField.dom.value = '';
16194 this.setRawValue('');
16195 this.lastSelectionText = '';
16196 this.lastData = false;
16198 var close = this.closeTriggerEl();
16209 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16210 * will be displayed in the field. If the value does not match the data value of an existing item,
16211 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16212 * Otherwise the field will be blank (although the value will still be set).
16213 * @param {String} value The value to match
16215 setValue : function(v)
16217 if(Roo.isIOS && this.useNativeIOS){
16218 this.setIOSValue(v);
16228 if(this.valueField){
16229 var r = this.findRecord(this.valueField, v);
16231 text = r.data[this.displayField];
16232 }else if(this.valueNotFoundText !== undefined){
16233 text = this.valueNotFoundText;
16236 this.lastSelectionText = text;
16237 if(this.hiddenField){
16238 this.hiddenField.dom.value = v;
16240 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16243 var close = this.closeTriggerEl();
16246 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16252 * @property {Object} the last set data for the element
16257 * Sets the value of the field based on a object which is related to the record format for the store.
16258 * @param {Object} value the value to set as. or false on reset?
16260 setFromData : function(o){
16267 var dv = ''; // display value
16268 var vv = ''; // value value..
16270 if (this.displayField) {
16271 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16273 // this is an error condition!!!
16274 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16277 if(this.valueField){
16278 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16281 var close = this.closeTriggerEl();
16284 if(dv.length || vv * 1 > 0){
16286 this.blockFocus=true;
16292 if(this.hiddenField){
16293 this.hiddenField.dom.value = vv;
16295 this.lastSelectionText = dv;
16296 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16300 // no hidden field.. - we store the value in 'value', but still display
16301 // display field!!!!
16302 this.lastSelectionText = dv;
16303 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16310 reset : function(){
16311 // overridden so that last data is reset..
16318 this.setValue(this.originalValue);
16319 //this.clearInvalid();
16320 this.lastData = false;
16322 this.view.clearSelections();
16328 findRecord : function(prop, value){
16330 if(this.store.getCount() > 0){
16331 this.store.each(function(r){
16332 if(r.data[prop] == value){
16342 getName: function()
16344 // returns hidden if it's set..
16345 if (!this.rendered) {return ''};
16346 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16350 onViewMove : function(e, t){
16351 this.inKeyMode = false;
16355 onViewOver : function(e, t){
16356 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16359 var item = this.view.findItemFromChild(t);
16362 var index = this.view.indexOf(item);
16363 this.select(index, false);
16368 onViewClick : function(view, doFocus, el, e)
16370 var index = this.view.getSelectedIndexes()[0];
16372 var r = this.store.getAt(index);
16376 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16383 Roo.each(this.tickItems, function(v,k){
16385 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16387 _this.tickItems.splice(k, 1);
16389 if(typeof(e) == 'undefined' && view == false){
16390 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16402 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16403 this.tickItems.push(r.data);
16406 if(typeof(e) == 'undefined' && view == false){
16407 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16414 this.onSelect(r, index);
16416 if(doFocus !== false && !this.blockFocus){
16417 this.inputEl().focus();
16422 restrictHeight : function(){
16423 //this.innerList.dom.style.height = '';
16424 //var inner = this.innerList.dom;
16425 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16426 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16427 //this.list.beginUpdate();
16428 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16429 this.list.alignTo(this.inputEl(), this.listAlign);
16430 this.list.alignTo(this.inputEl(), this.listAlign);
16431 //this.list.endUpdate();
16435 onEmptyResults : function(){
16437 if(this.tickable && this.editable){
16438 this.hasFocus = false;
16439 this.restrictHeight();
16447 * Returns true if the dropdown list is expanded, else false.
16449 isExpanded : function(){
16450 return this.list.isVisible();
16454 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16455 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16456 * @param {String} value The data value of the item to select
16457 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16458 * selected item if it is not currently in view (defaults to true)
16459 * @return {Boolean} True if the value matched an item in the list, else false
16461 selectByValue : function(v, scrollIntoView){
16462 if(v !== undefined && v !== null){
16463 var r = this.findRecord(this.valueField || this.displayField, v);
16465 this.select(this.store.indexOf(r), scrollIntoView);
16473 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16474 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16475 * @param {Number} index The zero-based index of the list item to select
16476 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16477 * selected item if it is not currently in view (defaults to true)
16479 select : function(index, scrollIntoView){
16480 this.selectedIndex = index;
16481 this.view.select(index);
16482 if(scrollIntoView !== false){
16483 var el = this.view.getNode(index);
16485 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16488 this.list.scrollChildIntoView(el, false);
16494 selectNext : function(){
16495 var ct = this.store.getCount();
16497 if(this.selectedIndex == -1){
16499 }else if(this.selectedIndex < ct-1){
16500 this.select(this.selectedIndex+1);
16506 selectPrev : function(){
16507 var ct = this.store.getCount();
16509 if(this.selectedIndex == -1){
16511 }else if(this.selectedIndex != 0){
16512 this.select(this.selectedIndex-1);
16518 onKeyUp : function(e){
16519 if(this.editable !== false && !e.isSpecialKey()){
16520 this.lastKey = e.getKey();
16521 this.dqTask.delay(this.queryDelay);
16526 validateBlur : function(){
16527 return !this.list || !this.list.isVisible();
16531 initQuery : function(){
16533 var v = this.getRawValue();
16535 if(this.tickable && this.editable){
16536 v = this.tickableInputEl().getValue();
16543 doForce : function(){
16544 if(this.inputEl().dom.value.length > 0){
16545 this.inputEl().dom.value =
16546 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16552 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16553 * query allowing the query action to be canceled if needed.
16554 * @param {String} query The SQL query to execute
16555 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16556 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16557 * saved in the current store (defaults to false)
16559 doQuery : function(q, forceAll){
16561 if(q === undefined || q === null){
16566 forceAll: forceAll,
16570 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16575 forceAll = qe.forceAll;
16576 if(forceAll === true || (q.length >= this.minChars)){
16578 this.hasQuery = true;
16580 if(this.lastQuery != q || this.alwaysQuery){
16581 this.lastQuery = q;
16582 if(this.mode == 'local'){
16583 this.selectedIndex = -1;
16585 this.store.clearFilter();
16588 if(this.specialFilter){
16589 this.fireEvent('specialfilter', this);
16594 this.store.filter(this.displayField, q);
16597 this.store.fireEvent("datachanged", this.store);
16604 this.store.baseParams[this.queryParam] = q;
16606 var options = {params : this.getParams(q)};
16609 options.add = true;
16610 options.params.start = this.page * this.pageSize;
16613 this.store.load(options);
16616 * this code will make the page width larger, at the beginning, the list not align correctly,
16617 * we should expand the list on onLoad
16618 * so command out it
16623 this.selectedIndex = -1;
16628 this.loadNext = false;
16632 getParams : function(q){
16634 //p[this.queryParam] = q;
16638 p.limit = this.pageSize;
16644 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16646 collapse : function(){
16647 if(!this.isExpanded()){
16653 this.hasFocus = false;
16657 this.cancelBtn.hide();
16658 this.trigger.show();
16661 this.tickableInputEl().dom.value = '';
16662 this.tickableInputEl().blur();
16667 Roo.get(document).un('mousedown', this.collapseIf, this);
16668 Roo.get(document).un('mousewheel', this.collapseIf, this);
16669 if (!this.editable) {
16670 Roo.get(document).un('keydown', this.listKeyPress, this);
16672 this.fireEvent('collapse', this);
16678 collapseIf : function(e){
16679 var in_combo = e.within(this.el);
16680 var in_list = e.within(this.list);
16681 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16683 if (in_combo || in_list || is_list) {
16684 //e.stopPropagation();
16689 this.onTickableFooterButtonClick(e, false, false);
16697 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16699 expand : function(){
16701 if(this.isExpanded() || !this.hasFocus){
16705 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16706 this.list.setWidth(lw);
16712 this.restrictHeight();
16716 this.tickItems = Roo.apply([], this.item);
16719 this.cancelBtn.show();
16720 this.trigger.hide();
16723 this.tickableInputEl().focus();
16728 Roo.get(document).on('mousedown', this.collapseIf, this);
16729 Roo.get(document).on('mousewheel', this.collapseIf, this);
16730 if (!this.editable) {
16731 Roo.get(document).on('keydown', this.listKeyPress, this);
16734 this.fireEvent('expand', this);
16738 // Implements the default empty TriggerField.onTriggerClick function
16739 onTriggerClick : function(e)
16741 Roo.log('trigger click');
16743 if(this.disabled || !this.triggerList){
16748 this.loadNext = false;
16750 if(this.isExpanded()){
16752 if (!this.blockFocus) {
16753 this.inputEl().focus();
16757 this.hasFocus = true;
16758 if(this.triggerAction == 'all') {
16759 this.doQuery(this.allQuery, true);
16761 this.doQuery(this.getRawValue());
16763 if (!this.blockFocus) {
16764 this.inputEl().focus();
16769 onTickableTriggerClick : function(e)
16776 this.loadNext = false;
16777 this.hasFocus = true;
16779 if(this.triggerAction == 'all') {
16780 this.doQuery(this.allQuery, true);
16782 this.doQuery(this.getRawValue());
16786 onSearchFieldClick : function(e)
16788 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16789 this.onTickableFooterButtonClick(e, false, false);
16793 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16798 this.loadNext = false;
16799 this.hasFocus = true;
16801 if(this.triggerAction == 'all') {
16802 this.doQuery(this.allQuery, true);
16804 this.doQuery(this.getRawValue());
16808 listKeyPress : function(e)
16810 //Roo.log('listkeypress');
16811 // scroll to first matching element based on key pres..
16812 if (e.isSpecialKey()) {
16815 var k = String.fromCharCode(e.getKey()).toUpperCase();
16818 var csel = this.view.getSelectedNodes();
16819 var cselitem = false;
16821 var ix = this.view.indexOf(csel[0]);
16822 cselitem = this.store.getAt(ix);
16823 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16829 this.store.each(function(v) {
16831 // start at existing selection.
16832 if (cselitem.id == v.id) {
16838 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16839 match = this.store.indexOf(v);
16845 if (match === false) {
16846 return true; // no more action?
16849 this.view.select(match);
16850 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16851 sn.scrollIntoView(sn.dom.parentNode, false);
16854 onViewScroll : function(e, t){
16856 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){
16860 this.hasQuery = true;
16862 this.loading = this.list.select('.loading', true).first();
16864 if(this.loading === null){
16865 this.list.createChild({
16867 cls: 'loading roo-select2-more-results roo-select2-active',
16868 html: 'Loading more results...'
16871 this.loading = this.list.select('.loading', true).first();
16873 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16875 this.loading.hide();
16878 this.loading.show();
16883 this.loadNext = true;
16885 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16890 addItem : function(o)
16892 var dv = ''; // display value
16894 if (this.displayField) {
16895 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16897 // this is an error condition!!!
16898 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16905 var choice = this.choices.createChild({
16907 cls: 'roo-select2-search-choice',
16916 cls: 'roo-select2-search-choice-close fa fa-times',
16921 }, this.searchField);
16923 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16925 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16933 this.inputEl().dom.value = '';
16938 onRemoveItem : function(e, _self, o)
16940 e.preventDefault();
16942 this.lastItem = Roo.apply([], this.item);
16944 var index = this.item.indexOf(o.data) * 1;
16947 Roo.log('not this item?!');
16951 this.item.splice(index, 1);
16956 this.fireEvent('remove', this, e);
16962 syncValue : function()
16964 if(!this.item.length){
16971 Roo.each(this.item, function(i){
16972 if(_this.valueField){
16973 value.push(i[_this.valueField]);
16980 this.value = value.join(',');
16982 if(this.hiddenField){
16983 this.hiddenField.dom.value = this.value;
16986 this.store.fireEvent("datachanged", this.store);
16991 clearItem : function()
16993 if(!this.multiple){
16999 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17007 if(this.tickable && !Roo.isTouch){
17008 this.view.refresh();
17012 inputEl: function ()
17014 if(Roo.isIOS && this.useNativeIOS){
17015 return this.el.select('select.roo-ios-select', true).first();
17018 if(Roo.isTouch && this.mobileTouchView){
17019 return this.el.select('input.form-control',true).first();
17023 return this.searchField;
17026 return this.el.select('input.form-control',true).first();
17029 onTickableFooterButtonClick : function(e, btn, el)
17031 e.preventDefault();
17033 this.lastItem = Roo.apply([], this.item);
17035 if(btn && btn.name == 'cancel'){
17036 this.tickItems = Roo.apply([], this.item);
17045 Roo.each(this.tickItems, function(o){
17053 validate : function()
17055 if(this.getVisibilityEl().hasClass('hidden')){
17059 var v = this.getRawValue();
17062 v = this.getValue();
17065 if(this.disabled || this.allowBlank || v.length){
17070 this.markInvalid();
17074 tickableInputEl : function()
17076 if(!this.tickable || !this.editable){
17077 return this.inputEl();
17080 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17084 getAutoCreateTouchView : function()
17089 cls: 'form-group' //input-group
17095 type : this.inputType,
17096 cls : 'form-control x-combo-noedit',
17097 autocomplete: 'new-password',
17098 placeholder : this.placeholder || '',
17103 input.name = this.name;
17107 input.cls += ' input-' + this.size;
17110 if (this.disabled) {
17111 input.disabled = true;
17115 cls : 'roo-combobox-wrap',
17122 inputblock.cls += ' input-group';
17124 inputblock.cn.unshift({
17126 cls : 'input-group-addon input-group-prepend input-group-text',
17131 if(this.removable && !this.multiple){
17132 inputblock.cls += ' roo-removable';
17134 inputblock.cn.push({
17137 cls : 'roo-combo-removable-btn close'
17141 if(this.hasFeedback && !this.allowBlank){
17143 inputblock.cls += ' has-feedback';
17145 inputblock.cn.push({
17147 cls: 'glyphicon form-control-feedback'
17154 inputblock.cls += (this.before) ? '' : ' input-group';
17156 inputblock.cn.push({
17158 cls : 'input-group-addon input-group-append input-group-text',
17164 var ibwrap = inputblock;
17169 cls: 'roo-select2-choices',
17173 cls: 'roo-select2-search-field',
17186 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17191 cls: 'form-hidden-field'
17197 if(!this.multiple && this.showToggleBtn){
17203 if (this.caret != false) {
17206 cls: 'fa fa-' + this.caret
17213 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17215 Roo.bootstrap.version == 3 ? caret : '',
17218 cls: 'combobox-clear',
17232 combobox.cls += ' roo-select2-container-multi';
17235 var align = this.labelAlign || this.parentLabelAlign();
17237 if (align ==='left' && this.fieldLabel.length) {
17242 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17243 tooltip : 'This field is required'
17247 cls : 'control-label col-form-label',
17248 html : this.fieldLabel
17252 cls : 'roo-combobox-wrap ',
17259 var labelCfg = cfg.cn[1];
17260 var contentCfg = cfg.cn[2];
17263 if(this.indicatorpos == 'right'){
17268 cls : 'control-label col-form-label',
17272 html : this.fieldLabel
17276 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17277 tooltip : 'This field is required'
17282 cls : "roo-combobox-wrap ",
17290 labelCfg = cfg.cn[0];
17291 contentCfg = cfg.cn[1];
17296 if(this.labelWidth > 12){
17297 labelCfg.style = "width: " + this.labelWidth + 'px';
17300 if(this.labelWidth < 13 && this.labelmd == 0){
17301 this.labelmd = this.labelWidth;
17304 if(this.labellg > 0){
17305 labelCfg.cls += ' col-lg-' + this.labellg;
17306 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17309 if(this.labelmd > 0){
17310 labelCfg.cls += ' col-md-' + this.labelmd;
17311 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17314 if(this.labelsm > 0){
17315 labelCfg.cls += ' col-sm-' + this.labelsm;
17316 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17319 if(this.labelxs > 0){
17320 labelCfg.cls += ' col-xs-' + this.labelxs;
17321 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17325 } else if ( this.fieldLabel.length) {
17329 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17330 tooltip : 'This field is required'
17334 cls : 'control-label',
17335 html : this.fieldLabel
17346 if(this.indicatorpos == 'right'){
17350 cls : 'control-label',
17351 html : this.fieldLabel,
17355 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17356 tooltip : 'This field is required'
17373 var settings = this;
17375 ['xs','sm','md','lg'].map(function(size){
17376 if (settings[size]) {
17377 cfg.cls += ' col-' + size + '-' + settings[size];
17384 initTouchView : function()
17386 this.renderTouchView();
17388 this.touchViewEl.on('scroll', function(){
17389 this.el.dom.scrollTop = 0;
17392 this.originalValue = this.getValue();
17394 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17396 this.inputEl().on("click", this.showTouchView, this);
17397 if (this.triggerEl) {
17398 this.triggerEl.on("click", this.showTouchView, this);
17402 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17403 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17405 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17407 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17408 this.store.on('load', this.onTouchViewLoad, this);
17409 this.store.on('loadexception', this.onTouchViewLoadException, this);
17411 if(this.hiddenName){
17413 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17415 this.hiddenField.dom.value =
17416 this.hiddenValue !== undefined ? this.hiddenValue :
17417 this.value !== undefined ? this.value : '';
17419 this.el.dom.removeAttribute('name');
17420 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17424 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17425 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17428 if(this.removable && !this.multiple){
17429 var close = this.closeTriggerEl();
17431 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17432 close.on('click', this.removeBtnClick, this, close);
17436 * fix the bug in Safari iOS8
17438 this.inputEl().on("focus", function(e){
17439 document.activeElement.blur();
17442 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17449 renderTouchView : function()
17451 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17452 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17454 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17455 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17457 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17458 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17459 this.touchViewBodyEl.setStyle('overflow', 'auto');
17461 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17462 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17464 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17465 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17469 showTouchView : function()
17475 this.touchViewHeaderEl.hide();
17477 if(this.modalTitle.length){
17478 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17479 this.touchViewHeaderEl.show();
17482 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17483 this.touchViewEl.show();
17485 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17487 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17488 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17490 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17492 if(this.modalTitle.length){
17493 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17496 this.touchViewBodyEl.setHeight(bodyHeight);
17500 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17502 this.touchViewEl.addClass(['in','show']);
17505 if(this._touchViewMask){
17506 Roo.get(document.body).addClass("x-body-masked");
17507 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17508 this._touchViewMask.setStyle('z-index', 10000);
17509 this._touchViewMask.addClass('show');
17512 this.doTouchViewQuery();
17516 hideTouchView : function()
17518 this.touchViewEl.removeClass(['in','show']);
17522 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17524 this.touchViewEl.setStyle('display', 'none');
17527 if(this._touchViewMask){
17528 this._touchViewMask.removeClass('show');
17529 Roo.get(document.body).removeClass("x-body-masked");
17533 setTouchViewValue : function()
17540 Roo.each(this.tickItems, function(o){
17545 this.hideTouchView();
17548 doTouchViewQuery : function()
17557 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17561 if(!this.alwaysQuery || this.mode == 'local'){
17562 this.onTouchViewLoad();
17569 onTouchViewBeforeLoad : function(combo,opts)
17575 onTouchViewLoad : function()
17577 if(this.store.getCount() < 1){
17578 this.onTouchViewEmptyResults();
17582 this.clearTouchView();
17584 var rawValue = this.getRawValue();
17586 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17588 this.tickItems = [];
17590 this.store.data.each(function(d, rowIndex){
17591 var row = this.touchViewListGroup.createChild(template);
17593 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17594 row.addClass(d.data.cls);
17597 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17600 html : d.data[this.displayField]
17603 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17604 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17607 row.removeClass('selected');
17608 if(!this.multiple && this.valueField &&
17609 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17612 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17613 row.addClass('selected');
17616 if(this.multiple && this.valueField &&
17617 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17621 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17622 this.tickItems.push(d.data);
17625 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17629 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17631 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17633 if(this.modalTitle.length){
17634 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17637 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17639 if(this.mobile_restrict_height && listHeight < bodyHeight){
17640 this.touchViewBodyEl.setHeight(listHeight);
17645 if(firstChecked && listHeight > bodyHeight){
17646 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17651 onTouchViewLoadException : function()
17653 this.hideTouchView();
17656 onTouchViewEmptyResults : function()
17658 this.clearTouchView();
17660 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17662 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17666 clearTouchView : function()
17668 this.touchViewListGroup.dom.innerHTML = '';
17671 onTouchViewClick : function(e, el, o)
17673 e.preventDefault();
17676 var rowIndex = o.rowIndex;
17678 var r = this.store.getAt(rowIndex);
17680 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17682 if(!this.multiple){
17683 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17684 c.dom.removeAttribute('checked');
17687 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689 this.setFromData(r.data);
17691 var close = this.closeTriggerEl();
17697 this.hideTouchView();
17699 this.fireEvent('select', this, r, rowIndex);
17704 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17705 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17706 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17710 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17711 this.addItem(r.data);
17712 this.tickItems.push(r.data);
17716 getAutoCreateNativeIOS : function()
17719 cls: 'form-group' //input-group,
17724 cls : 'roo-ios-select'
17728 combobox.name = this.name;
17731 if (this.disabled) {
17732 combobox.disabled = true;
17735 var settings = this;
17737 ['xs','sm','md','lg'].map(function(size){
17738 if (settings[size]) {
17739 cfg.cls += ' col-' + size + '-' + settings[size];
17749 initIOSView : function()
17751 this.store.on('load', this.onIOSViewLoad, this);
17756 onIOSViewLoad : function()
17758 if(this.store.getCount() < 1){
17762 this.clearIOSView();
17764 if(this.allowBlank) {
17766 var default_text = '-- SELECT --';
17768 if(this.placeholder.length){
17769 default_text = this.placeholder;
17772 if(this.emptyTitle.length){
17773 default_text += ' - ' + this.emptyTitle + ' -';
17776 var opt = this.inputEl().createChild({
17779 html : default_text
17783 o[this.valueField] = 0;
17784 o[this.displayField] = default_text;
17786 this.ios_options.push({
17793 this.store.data.each(function(d, rowIndex){
17797 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17798 html = d.data[this.displayField];
17803 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17804 value = d.data[this.valueField];
17813 if(this.value == d.data[this.valueField]){
17814 option['selected'] = true;
17817 var opt = this.inputEl().createChild(option);
17819 this.ios_options.push({
17826 this.inputEl().on('change', function(){
17827 this.fireEvent('select', this);
17832 clearIOSView: function()
17834 this.inputEl().dom.innerHTML = '';
17836 this.ios_options = [];
17839 setIOSValue: function(v)
17843 if(!this.ios_options){
17847 Roo.each(this.ios_options, function(opts){
17849 opts.el.dom.removeAttribute('selected');
17851 if(opts.data[this.valueField] != v){
17855 opts.el.dom.setAttribute('selected', true);
17861 * @cfg {Boolean} grow
17865 * @cfg {Number} growMin
17869 * @cfg {Number} growMax
17878 Roo.apply(Roo.bootstrap.ComboBox, {
17882 cls: 'modal-header',
17904 cls: 'list-group-item',
17908 cls: 'roo-combobox-list-group-item-value'
17912 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17926 listItemCheckbox : {
17928 cls: 'list-group-item',
17932 cls: 'roo-combobox-list-group-item-value'
17936 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17952 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17957 cls: 'modal-footer',
17965 cls: 'col-xs-6 text-left',
17968 cls: 'btn btn-danger roo-touch-view-cancel',
17974 cls: 'col-xs-6 text-right',
17977 cls: 'btn btn-success roo-touch-view-ok',
17988 Roo.apply(Roo.bootstrap.ComboBox, {
17990 touchViewTemplate : {
17992 cls: 'modal fade roo-combobox-touch-view',
17996 cls: 'modal-dialog',
17997 style : 'position:fixed', // we have to fix position....
18001 cls: 'modal-content',
18003 Roo.bootstrap.ComboBox.header,
18004 Roo.bootstrap.ComboBox.body,
18005 Roo.bootstrap.ComboBox.footer
18014 * Ext JS Library 1.1.1
18015 * Copyright(c) 2006-2007, Ext JS, LLC.
18017 * Originally Released Under LGPL - original licence link has changed is not relivant.
18020 * <script type="text/javascript">
18025 * @extends Roo.util.Observable
18026 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18027 * This class also supports single and multi selection modes. <br>
18028 * Create a data model bound view:
18030 var store = new Roo.data.Store(...);
18032 var view = new Roo.View({
18034 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18036 singleSelect: true,
18037 selectedClass: "ydataview-selected",
18041 // listen for node click?
18042 view.on("click", function(vw, index, node, e){
18043 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18047 dataModel.load("foobar.xml");
18049 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18051 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18052 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18054 * Note: old style constructor is still suported (container, template, config)
18057 * Create a new View
18058 * @param {Object} config The config object
18061 Roo.View = function(config, depreciated_tpl, depreciated_config){
18063 this.parent = false;
18065 if (typeof(depreciated_tpl) == 'undefined') {
18066 // new way.. - universal constructor.
18067 Roo.apply(this, config);
18068 this.el = Roo.get(this.el);
18071 this.el = Roo.get(config);
18072 this.tpl = depreciated_tpl;
18073 Roo.apply(this, depreciated_config);
18075 this.wrapEl = this.el.wrap().wrap();
18076 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18079 if(typeof(this.tpl) == "string"){
18080 this.tpl = new Roo.Template(this.tpl);
18082 // support xtype ctors..
18083 this.tpl = new Roo.factory(this.tpl, Roo);
18087 this.tpl.compile();
18092 * @event beforeclick
18093 * Fires before a click is processed. Returns false to cancel the default action.
18094 * @param {Roo.View} this
18095 * @param {Number} index The index of the target node
18096 * @param {HTMLElement} node The target node
18097 * @param {Roo.EventObject} e The raw event object
18099 "beforeclick" : true,
18102 * Fires when a template node is clicked.
18103 * @param {Roo.View} this
18104 * @param {Number} index The index of the target node
18105 * @param {HTMLElement} node The target node
18106 * @param {Roo.EventObject} e The raw event object
18111 * Fires when a template node is double clicked.
18112 * @param {Roo.View} this
18113 * @param {Number} index The index of the target node
18114 * @param {HTMLElement} node The target node
18115 * @param {Roo.EventObject} e The raw event object
18119 * @event contextmenu
18120 * Fires when a template node is right clicked.
18121 * @param {Roo.View} this
18122 * @param {Number} index The index of the target node
18123 * @param {HTMLElement} node The target node
18124 * @param {Roo.EventObject} e The raw event object
18126 "contextmenu" : true,
18128 * @event selectionchange
18129 * Fires when the selected nodes change.
18130 * @param {Roo.View} this
18131 * @param {Array} selections Array of the selected nodes
18133 "selectionchange" : true,
18136 * @event beforeselect
18137 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18138 * @param {Roo.View} this
18139 * @param {HTMLElement} node The node to be selected
18140 * @param {Array} selections Array of currently selected nodes
18142 "beforeselect" : true,
18144 * @event preparedata
18145 * Fires on every row to render, to allow you to change the data.
18146 * @param {Roo.View} this
18147 * @param {Object} data to be rendered (change this)
18149 "preparedata" : true
18157 "click": this.onClick,
18158 "dblclick": this.onDblClick,
18159 "contextmenu": this.onContextMenu,
18163 this.selections = [];
18165 this.cmp = new Roo.CompositeElementLite([]);
18167 this.store = Roo.factory(this.store, Roo.data);
18168 this.setStore(this.store, true);
18171 if ( this.footer && this.footer.xtype) {
18173 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18175 this.footer.dataSource = this.store;
18176 this.footer.container = fctr;
18177 this.footer = Roo.factory(this.footer, Roo);
18178 fctr.insertFirst(this.el);
18180 // this is a bit insane - as the paging toolbar seems to detach the el..
18181 // dom.parentNode.parentNode.parentNode
18182 // they get detached?
18186 Roo.View.superclass.constructor.call(this);
18191 Roo.extend(Roo.View, Roo.util.Observable, {
18194 * @cfg {Roo.data.Store} store Data store to load data from.
18199 * @cfg {String|Roo.Element} el The container element.
18204 * @cfg {String|Roo.Template} tpl The template used by this View
18208 * @cfg {String} dataName the named area of the template to use as the data area
18209 * Works with domtemplates roo-name="name"
18213 * @cfg {String} selectedClass The css class to add to selected nodes
18215 selectedClass : "x-view-selected",
18217 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18222 * @cfg {String} text to display on mask (default Loading)
18226 * @cfg {Boolean} multiSelect Allow multiple selection
18228 multiSelect : false,
18230 * @cfg {Boolean} singleSelect Allow single selection
18232 singleSelect: false,
18235 * @cfg {Boolean} toggleSelect - selecting
18237 toggleSelect : false,
18240 * @cfg {Boolean} tickable - selecting
18245 * Returns the element this view is bound to.
18246 * @return {Roo.Element}
18248 getEl : function(){
18249 return this.wrapEl;
18255 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18257 refresh : function(){
18258 //Roo.log('refresh');
18261 // if we are using something like 'domtemplate', then
18262 // the what gets used is:
18263 // t.applySubtemplate(NAME, data, wrapping data..)
18264 // the outer template then get' applied with
18265 // the store 'extra data'
18266 // and the body get's added to the
18267 // roo-name="data" node?
18268 // <span class='roo-tpl-{name}'></span> ?????
18272 this.clearSelections();
18273 this.el.update("");
18275 var records = this.store.getRange();
18276 if(records.length < 1) {
18278 // is this valid?? = should it render a template??
18280 this.el.update(this.emptyText);
18284 if (this.dataName) {
18285 this.el.update(t.apply(this.store.meta)); //????
18286 el = this.el.child('.roo-tpl-' + this.dataName);
18289 for(var i = 0, len = records.length; i < len; i++){
18290 var data = this.prepareData(records[i].data, i, records[i]);
18291 this.fireEvent("preparedata", this, data, i, records[i]);
18293 var d = Roo.apply({}, data);
18296 Roo.apply(d, {'roo-id' : Roo.id()});
18300 Roo.each(this.parent.item, function(item){
18301 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18304 Roo.apply(d, {'roo-data-checked' : 'checked'});
18308 html[html.length] = Roo.util.Format.trim(
18310 t.applySubtemplate(this.dataName, d, this.store.meta) :
18317 el.update(html.join(""));
18318 this.nodes = el.dom.childNodes;
18319 this.updateIndexes(0);
18324 * Function to override to reformat the data that is sent to
18325 * the template for each node.
18326 * DEPRICATED - use the preparedata event handler.
18327 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18328 * a JSON object for an UpdateManager bound view).
18330 prepareData : function(data, index, record)
18332 this.fireEvent("preparedata", this, data, index, record);
18336 onUpdate : function(ds, record){
18337 // Roo.log('on update');
18338 this.clearSelections();
18339 var index = this.store.indexOf(record);
18340 var n = this.nodes[index];
18341 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18342 n.parentNode.removeChild(n);
18343 this.updateIndexes(index, index);
18349 onAdd : function(ds, records, index)
18351 //Roo.log(['on Add', ds, records, index] );
18352 this.clearSelections();
18353 if(this.nodes.length == 0){
18357 var n = this.nodes[index];
18358 for(var i = 0, len = records.length; i < len; i++){
18359 var d = this.prepareData(records[i].data, i, records[i]);
18361 this.tpl.insertBefore(n, d);
18364 this.tpl.append(this.el, d);
18367 this.updateIndexes(index);
18370 onRemove : function(ds, record, index){
18371 // Roo.log('onRemove');
18372 this.clearSelections();
18373 var el = this.dataName ?
18374 this.el.child('.roo-tpl-' + this.dataName) :
18377 el.dom.removeChild(this.nodes[index]);
18378 this.updateIndexes(index);
18382 * Refresh an individual node.
18383 * @param {Number} index
18385 refreshNode : function(index){
18386 this.onUpdate(this.store, this.store.getAt(index));
18389 updateIndexes : function(startIndex, endIndex){
18390 var ns = this.nodes;
18391 startIndex = startIndex || 0;
18392 endIndex = endIndex || ns.length - 1;
18393 for(var i = startIndex; i <= endIndex; i++){
18394 ns[i].nodeIndex = i;
18399 * Changes the data store this view uses and refresh the view.
18400 * @param {Store} store
18402 setStore : function(store, initial){
18403 if(!initial && this.store){
18404 this.store.un("datachanged", this.refresh);
18405 this.store.un("add", this.onAdd);
18406 this.store.un("remove", this.onRemove);
18407 this.store.un("update", this.onUpdate);
18408 this.store.un("clear", this.refresh);
18409 this.store.un("beforeload", this.onBeforeLoad);
18410 this.store.un("load", this.onLoad);
18411 this.store.un("loadexception", this.onLoad);
18415 store.on("datachanged", this.refresh, this);
18416 store.on("add", this.onAdd, this);
18417 store.on("remove", this.onRemove, this);
18418 store.on("update", this.onUpdate, this);
18419 store.on("clear", this.refresh, this);
18420 store.on("beforeload", this.onBeforeLoad, this);
18421 store.on("load", this.onLoad, this);
18422 store.on("loadexception", this.onLoad, this);
18430 * onbeforeLoad - masks the loading area.
18433 onBeforeLoad : function(store,opts)
18435 //Roo.log('onBeforeLoad');
18437 this.el.update("");
18439 this.el.mask(this.mask ? this.mask : "Loading" );
18441 onLoad : function ()
18448 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18449 * @param {HTMLElement} node
18450 * @return {HTMLElement} The template node
18452 findItemFromChild : function(node){
18453 var el = this.dataName ?
18454 this.el.child('.roo-tpl-' + this.dataName,true) :
18457 if(!node || node.parentNode == el){
18460 var p = node.parentNode;
18461 while(p && p != el){
18462 if(p.parentNode == el){
18471 onClick : function(e){
18472 var item = this.findItemFromChild(e.getTarget());
18474 var index = this.indexOf(item);
18475 if(this.onItemClick(item, index, e) !== false){
18476 this.fireEvent("click", this, index, item, e);
18479 this.clearSelections();
18484 onContextMenu : function(e){
18485 var item = this.findItemFromChild(e.getTarget());
18487 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18492 onDblClick : function(e){
18493 var item = this.findItemFromChild(e.getTarget());
18495 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18499 onItemClick : function(item, index, e)
18501 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18504 if (this.toggleSelect) {
18505 var m = this.isSelected(item) ? 'unselect' : 'select';
18508 _t[m](item, true, false);
18511 if(this.multiSelect || this.singleSelect){
18512 if(this.multiSelect && e.shiftKey && this.lastSelection){
18513 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18515 this.select(item, this.multiSelect && e.ctrlKey);
18516 this.lastSelection = item;
18519 if(!this.tickable){
18520 e.preventDefault();
18528 * Get the number of selected nodes.
18531 getSelectionCount : function(){
18532 return this.selections.length;
18536 * Get the currently selected nodes.
18537 * @return {Array} An array of HTMLElements
18539 getSelectedNodes : function(){
18540 return this.selections;
18544 * Get the indexes of the selected nodes.
18547 getSelectedIndexes : function(){
18548 var indexes = [], s = this.selections;
18549 for(var i = 0, len = s.length; i < len; i++){
18550 indexes.push(s[i].nodeIndex);
18556 * Clear all selections
18557 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18559 clearSelections : function(suppressEvent){
18560 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18561 this.cmp.elements = this.selections;
18562 this.cmp.removeClass(this.selectedClass);
18563 this.selections = [];
18564 if(!suppressEvent){
18565 this.fireEvent("selectionchange", this, this.selections);
18571 * Returns true if the passed node is selected
18572 * @param {HTMLElement/Number} node The node or node index
18573 * @return {Boolean}
18575 isSelected : function(node){
18576 var s = this.selections;
18580 node = this.getNode(node);
18581 return s.indexOf(node) !== -1;
18586 * @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
18587 * @param {Boolean} keepExisting (optional) true to keep existing selections
18588 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18590 select : function(nodeInfo, keepExisting, suppressEvent){
18591 if(nodeInfo instanceof Array){
18593 this.clearSelections(true);
18595 for(var i = 0, len = nodeInfo.length; i < len; i++){
18596 this.select(nodeInfo[i], true, true);
18600 var node = this.getNode(nodeInfo);
18601 if(!node || this.isSelected(node)){
18602 return; // already selected.
18605 this.clearSelections(true);
18608 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18609 Roo.fly(node).addClass(this.selectedClass);
18610 this.selections.push(node);
18611 if(!suppressEvent){
18612 this.fireEvent("selectionchange", this, this.selections);
18620 * @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
18621 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18622 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18624 unselect : function(nodeInfo, keepExisting, suppressEvent)
18626 if(nodeInfo instanceof Array){
18627 Roo.each(this.selections, function(s) {
18628 this.unselect(s, nodeInfo);
18632 var node = this.getNode(nodeInfo);
18633 if(!node || !this.isSelected(node)){
18634 //Roo.log("not selected");
18635 return; // not selected.
18639 Roo.each(this.selections, function(s) {
18641 Roo.fly(node).removeClass(this.selectedClass);
18648 this.selections= ns;
18649 this.fireEvent("selectionchange", this, this.selections);
18653 * Gets a template node.
18654 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18655 * @return {HTMLElement} The node or null if it wasn't found
18657 getNode : function(nodeInfo){
18658 if(typeof nodeInfo == "string"){
18659 return document.getElementById(nodeInfo);
18660 }else if(typeof nodeInfo == "number"){
18661 return this.nodes[nodeInfo];
18667 * Gets a range template nodes.
18668 * @param {Number} startIndex
18669 * @param {Number} endIndex
18670 * @return {Array} An array of nodes
18672 getNodes : function(start, end){
18673 var ns = this.nodes;
18674 start = start || 0;
18675 end = typeof end == "undefined" ? ns.length - 1 : end;
18678 for(var i = start; i <= end; i++){
18682 for(var i = start; i >= end; i--){
18690 * Finds the index of the passed node
18691 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18692 * @return {Number} The index of the node or -1
18694 indexOf : function(node){
18695 node = this.getNode(node);
18696 if(typeof node.nodeIndex == "number"){
18697 return node.nodeIndex;
18699 var ns = this.nodes;
18700 for(var i = 0, len = ns.length; i < len; i++){
18711 * based on jquery fullcalendar
18715 Roo.bootstrap = Roo.bootstrap || {};
18717 * @class Roo.bootstrap.Calendar
18718 * @extends Roo.bootstrap.Component
18719 * Bootstrap Calendar class
18720 * @cfg {Boolean} loadMask (true|false) default false
18721 * @cfg {Object} header generate the user specific header of the calendar, default false
18724 * Create a new Container
18725 * @param {Object} config The config object
18730 Roo.bootstrap.Calendar = function(config){
18731 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18735 * Fires when a date is selected
18736 * @param {DatePicker} this
18737 * @param {Date} date The selected date
18741 * @event monthchange
18742 * Fires when the displayed month changes
18743 * @param {DatePicker} this
18744 * @param {Date} date The selected month
18746 'monthchange': true,
18748 * @event evententer
18749 * Fires when mouse over an event
18750 * @param {Calendar} this
18751 * @param {event} Event
18753 'evententer': true,
18755 * @event eventleave
18756 * Fires when the mouse leaves an
18757 * @param {Calendar} this
18760 'eventleave': true,
18762 * @event eventclick
18763 * Fires when the mouse click an
18764 * @param {Calendar} this
18773 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18776 * @cfg {Number} startDay
18777 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18785 getAutoCreate : function(){
18788 var fc_button = function(name, corner, style, content ) {
18789 return Roo.apply({},{
18791 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18793 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18796 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18807 style : 'width:100%',
18814 cls : 'fc-header-left',
18816 fc_button('prev', 'left', 'arrow', '‹' ),
18817 fc_button('next', 'right', 'arrow', '›' ),
18818 { tag: 'span', cls: 'fc-header-space' },
18819 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18827 cls : 'fc-header-center',
18831 cls: 'fc-header-title',
18834 html : 'month / year'
18842 cls : 'fc-header-right',
18844 /* fc_button('month', 'left', '', 'month' ),
18845 fc_button('week', '', '', 'week' ),
18846 fc_button('day', 'right', '', 'day' )
18858 header = this.header;
18861 var cal_heads = function() {
18863 // fixme - handle this.
18865 for (var i =0; i < Date.dayNames.length; i++) {
18866 var d = Date.dayNames[i];
18869 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18870 html : d.substring(0,3)
18874 ret[0].cls += ' fc-first';
18875 ret[6].cls += ' fc-last';
18878 var cal_cell = function(n) {
18881 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18886 cls: 'fc-day-number',
18890 cls: 'fc-day-content',
18894 style: 'position: relative;' // height: 17px;
18906 var cal_rows = function() {
18909 for (var r = 0; r < 6; r++) {
18916 for (var i =0; i < Date.dayNames.length; i++) {
18917 var d = Date.dayNames[i];
18918 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18921 row.cn[0].cls+=' fc-first';
18922 row.cn[0].cn[0].style = 'min-height:90px';
18923 row.cn[6].cls+=' fc-last';
18927 ret[0].cls += ' fc-first';
18928 ret[4].cls += ' fc-prev-last';
18929 ret[5].cls += ' fc-last';
18936 cls: 'fc-border-separate',
18937 style : 'width:100%',
18945 cls : 'fc-first fc-last',
18963 cls : 'fc-content',
18964 style : "position: relative;",
18967 cls : 'fc-view fc-view-month fc-grid',
18968 style : 'position: relative',
18969 unselectable : 'on',
18972 cls : 'fc-event-container',
18973 style : 'position:absolute;z-index:8;top:0;left:0;'
18991 initEvents : function()
18994 throw "can not find store for calendar";
19000 style: "text-align:center",
19004 style: "background-color:white;width:50%;margin:250 auto",
19008 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19019 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19021 var size = this.el.select('.fc-content', true).first().getSize();
19022 this.maskEl.setSize(size.width, size.height);
19023 this.maskEl.enableDisplayMode("block");
19024 if(!this.loadMask){
19025 this.maskEl.hide();
19028 this.store = Roo.factory(this.store, Roo.data);
19029 this.store.on('load', this.onLoad, this);
19030 this.store.on('beforeload', this.onBeforeLoad, this);
19034 this.cells = this.el.select('.fc-day',true);
19035 //Roo.log(this.cells);
19036 this.textNodes = this.el.query('.fc-day-number');
19037 this.cells.addClassOnOver('fc-state-hover');
19039 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19040 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19041 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19042 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19044 this.on('monthchange', this.onMonthChange, this);
19046 this.update(new Date().clearTime());
19049 resize : function() {
19050 var sz = this.el.getSize();
19052 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19053 this.el.select('.fc-day-content div',true).setHeight(34);
19058 showPrevMonth : function(e){
19059 this.update(this.activeDate.add("mo", -1));
19061 showToday : function(e){
19062 this.update(new Date().clearTime());
19065 showNextMonth : function(e){
19066 this.update(this.activeDate.add("mo", 1));
19070 showPrevYear : function(){
19071 this.update(this.activeDate.add("y", -1));
19075 showNextYear : function(){
19076 this.update(this.activeDate.add("y", 1));
19081 update : function(date)
19083 var vd = this.activeDate;
19084 this.activeDate = date;
19085 // if(vd && this.el){
19086 // var t = date.getTime();
19087 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19088 // Roo.log('using add remove');
19090 // this.fireEvent('monthchange', this, date);
19092 // this.cells.removeClass("fc-state-highlight");
19093 // this.cells.each(function(c){
19094 // if(c.dateValue == t){
19095 // c.addClass("fc-state-highlight");
19096 // setTimeout(function(){
19097 // try{c.dom.firstChild.focus();}catch(e){}
19107 var days = date.getDaysInMonth();
19109 var firstOfMonth = date.getFirstDateOfMonth();
19110 var startingPos = firstOfMonth.getDay()-this.startDay;
19112 if(startingPos < this.startDay){
19116 var pm = date.add(Date.MONTH, -1);
19117 var prevStart = pm.getDaysInMonth()-startingPos;
19119 this.cells = this.el.select('.fc-day',true);
19120 this.textNodes = this.el.query('.fc-day-number');
19121 this.cells.addClassOnOver('fc-state-hover');
19123 var cells = this.cells.elements;
19124 var textEls = this.textNodes;
19126 Roo.each(cells, function(cell){
19127 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19130 days += startingPos;
19132 // convert everything to numbers so it's fast
19133 var day = 86400000;
19134 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19137 //Roo.log(prevStart);
19139 var today = new Date().clearTime().getTime();
19140 var sel = date.clearTime().getTime();
19141 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19142 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19143 var ddMatch = this.disabledDatesRE;
19144 var ddText = this.disabledDatesText;
19145 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19146 var ddaysText = this.disabledDaysText;
19147 var format = this.format;
19149 var setCellClass = function(cal, cell){
19153 //Roo.log('set Cell Class');
19155 var t = d.getTime();
19159 cell.dateValue = t;
19161 cell.className += " fc-today";
19162 cell.className += " fc-state-highlight";
19163 cell.title = cal.todayText;
19166 // disable highlight in other month..
19167 //cell.className += " fc-state-highlight";
19172 cell.className = " fc-state-disabled";
19173 cell.title = cal.minText;
19177 cell.className = " fc-state-disabled";
19178 cell.title = cal.maxText;
19182 if(ddays.indexOf(d.getDay()) != -1){
19183 cell.title = ddaysText;
19184 cell.className = " fc-state-disabled";
19187 if(ddMatch && format){
19188 var fvalue = d.dateFormat(format);
19189 if(ddMatch.test(fvalue)){
19190 cell.title = ddText.replace("%0", fvalue);
19191 cell.className = " fc-state-disabled";
19195 if (!cell.initialClassName) {
19196 cell.initialClassName = cell.dom.className;
19199 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19204 for(; i < startingPos; i++) {
19205 textEls[i].innerHTML = (++prevStart);
19206 d.setDate(d.getDate()+1);
19208 cells[i].className = "fc-past fc-other-month";
19209 setCellClass(this, cells[i]);
19214 for(; i < days; i++){
19215 intDay = i - startingPos + 1;
19216 textEls[i].innerHTML = (intDay);
19217 d.setDate(d.getDate()+1);
19219 cells[i].className = ''; // "x-date-active";
19220 setCellClass(this, cells[i]);
19224 for(; i < 42; i++) {
19225 textEls[i].innerHTML = (++extraDays);
19226 d.setDate(d.getDate()+1);
19228 cells[i].className = "fc-future fc-other-month";
19229 setCellClass(this, cells[i]);
19232 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19234 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19236 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19237 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19239 if(totalRows != 6){
19240 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19241 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19244 this.fireEvent('monthchange', this, date);
19248 if(!this.internalRender){
19249 var main = this.el.dom.firstChild;
19250 var w = main.offsetWidth;
19251 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19252 Roo.fly(main).setWidth(w);
19253 this.internalRender = true;
19254 // opera does not respect the auto grow header center column
19255 // then, after it gets a width opera refuses to recalculate
19256 // without a second pass
19257 if(Roo.isOpera && !this.secondPass){
19258 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19259 this.secondPass = true;
19260 this.update.defer(10, this, [date]);
19267 findCell : function(dt) {
19268 dt = dt.clearTime().getTime();
19270 this.cells.each(function(c){
19271 //Roo.log("check " +c.dateValue + '?=' + dt);
19272 if(c.dateValue == dt){
19282 findCells : function(ev) {
19283 var s = ev.start.clone().clearTime().getTime();
19285 var e= ev.end.clone().clearTime().getTime();
19288 this.cells.each(function(c){
19289 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19291 if(c.dateValue > e){
19294 if(c.dateValue < s){
19303 // findBestRow: function(cells)
19307 // for (var i =0 ; i < cells.length;i++) {
19308 // ret = Math.max(cells[i].rows || 0,ret);
19315 addItem : function(ev)
19317 // look for vertical location slot in
19318 var cells = this.findCells(ev);
19320 // ev.row = this.findBestRow(cells);
19322 // work out the location.
19326 for(var i =0; i < cells.length; i++) {
19328 cells[i].row = cells[0].row;
19331 cells[i].row = cells[i].row + 1;
19341 if (crow.start.getY() == cells[i].getY()) {
19343 crow.end = cells[i];
19360 cells[0].events.push(ev);
19362 this.calevents.push(ev);
19365 clearEvents: function() {
19367 if(!this.calevents){
19371 Roo.each(this.cells.elements, function(c){
19377 Roo.each(this.calevents, function(e) {
19378 Roo.each(e.els, function(el) {
19379 el.un('mouseenter' ,this.onEventEnter, this);
19380 el.un('mouseleave' ,this.onEventLeave, this);
19385 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19391 renderEvents: function()
19395 this.cells.each(function(c) {
19404 if(c.row != c.events.length){
19405 r = 4 - (4 - (c.row - c.events.length));
19408 c.events = ev.slice(0, r);
19409 c.more = ev.slice(r);
19411 if(c.more.length && c.more.length == 1){
19412 c.events.push(c.more.pop());
19415 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19419 this.cells.each(function(c) {
19421 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19424 for (var e = 0; e < c.events.length; e++){
19425 var ev = c.events[e];
19426 var rows = ev.rows;
19428 for(var i = 0; i < rows.length; i++) {
19430 // how many rows should it span..
19433 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19434 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19436 unselectable : "on",
19439 cls: 'fc-event-inner',
19443 // cls: 'fc-event-time',
19444 // html : cells.length > 1 ? '' : ev.time
19448 cls: 'fc-event-title',
19449 html : String.format('{0}', ev.title)
19456 cls: 'ui-resizable-handle ui-resizable-e',
19457 html : '  '
19464 cfg.cls += ' fc-event-start';
19466 if ((i+1) == rows.length) {
19467 cfg.cls += ' fc-event-end';
19470 var ctr = _this.el.select('.fc-event-container',true).first();
19471 var cg = ctr.createChild(cfg);
19473 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19474 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19476 var r = (c.more.length) ? 1 : 0;
19477 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19478 cg.setWidth(ebox.right - sbox.x -2);
19480 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19481 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19482 cg.on('click', _this.onEventClick, _this, ev);
19493 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19494 style : 'position: absolute',
19495 unselectable : "on",
19498 cls: 'fc-event-inner',
19502 cls: 'fc-event-title',
19510 cls: 'ui-resizable-handle ui-resizable-e',
19511 html : '  '
19517 var ctr = _this.el.select('.fc-event-container',true).first();
19518 var cg = ctr.createChild(cfg);
19520 var sbox = c.select('.fc-day-content',true).first().getBox();
19521 var ebox = c.select('.fc-day-content',true).first().getBox();
19523 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19524 cg.setWidth(ebox.right - sbox.x -2);
19526 cg.on('click', _this.onMoreEventClick, _this, c.more);
19536 onEventEnter: function (e, el,event,d) {
19537 this.fireEvent('evententer', this, el, event);
19540 onEventLeave: function (e, el,event,d) {
19541 this.fireEvent('eventleave', this, el, event);
19544 onEventClick: function (e, el,event,d) {
19545 this.fireEvent('eventclick', this, el, event);
19548 onMonthChange: function () {
19552 onMoreEventClick: function(e, el, more)
19556 this.calpopover.placement = 'right';
19557 this.calpopover.setTitle('More');
19559 this.calpopover.setContent('');
19561 var ctr = this.calpopover.el.select('.popover-content', true).first();
19563 Roo.each(more, function(m){
19565 cls : 'fc-event-hori fc-event-draggable',
19568 var cg = ctr.createChild(cfg);
19570 cg.on('click', _this.onEventClick, _this, m);
19573 this.calpopover.show(el);
19578 onLoad: function ()
19580 this.calevents = [];
19583 if(this.store.getCount() > 0){
19584 this.store.data.each(function(d){
19587 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19588 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19589 time : d.data.start_time,
19590 title : d.data.title,
19591 description : d.data.description,
19592 venue : d.data.venue
19597 this.renderEvents();
19599 if(this.calevents.length && this.loadMask){
19600 this.maskEl.hide();
19604 onBeforeLoad: function()
19606 this.clearEvents();
19608 this.maskEl.show();
19622 * @class Roo.bootstrap.Popover
19623 * @extends Roo.bootstrap.Component
19624 * Bootstrap Popover class
19625 * @cfg {String} html contents of the popover (or false to use children..)
19626 * @cfg {String} title of popover (or false to hide)
19627 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19628 * @cfg {String} trigger click || hover (or false to trigger manually)
19629 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19630 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19631 * - if false and it has a 'parent' then it will be automatically added to that element
19632 * - if string - Roo.get will be called
19633 * @cfg {Number} delay - delay before showing
19636 * Create a new Popover
19637 * @param {Object} config The config object
19640 Roo.bootstrap.Popover = function(config){
19641 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19647 * After the popover show
19649 * @param {Roo.bootstrap.Popover} this
19654 * After the popover hide
19656 * @param {Roo.bootstrap.Popover} this
19662 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19667 placement : 'right',
19668 trigger : 'hover', // hover
19674 can_build_overlaid : false,
19676 maskEl : false, // the mask element
19679 alignEl : false, // when show is called with an element - this get's stored.
19681 getChildContainer : function()
19683 return this.contentEl;
19686 getPopoverHeader : function()
19688 this.title = true; // flag not to hide it..
19689 this.headerEl.addClass('p-0');
19690 return this.headerEl
19694 getAutoCreate : function(){
19697 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19698 style: 'display:block',
19704 cls : 'popover-inner ',
19708 cls: 'popover-title popover-header',
19709 html : this.title === false ? '' : this.title
19712 cls : 'popover-content popover-body ' + (this.cls || ''),
19713 html : this.html || ''
19724 * @param {string} the title
19726 setTitle: function(str)
19730 this.headerEl.dom.innerHTML = str;
19735 * @param {string} the body content
19737 setContent: function(str)
19740 if (this.contentEl) {
19741 this.contentEl.dom.innerHTML = str;
19745 // as it get's added to the bottom of the page.
19746 onRender : function(ct, position)
19748 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19753 var cfg = Roo.apply({}, this.getAutoCreate());
19757 cfg.cls += ' ' + this.cls;
19760 cfg.style = this.style;
19762 //Roo.log("adding to ");
19763 this.el = Roo.get(document.body).createChild(cfg, position);
19764 // Roo.log(this.el);
19767 this.contentEl = this.el.select('.popover-content',true).first();
19768 this.headerEl = this.el.select('.popover-title',true).first();
19771 if(typeof(this.items) != 'undefined'){
19772 var items = this.items;
19775 for(var i =0;i < items.length;i++) {
19776 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19780 this.items = nitems;
19782 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19783 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19790 resizeMask : function()
19792 this.maskEl.setSize(
19793 Roo.lib.Dom.getViewWidth(true),
19794 Roo.lib.Dom.getViewHeight(true)
19798 initEvents : function()
19802 Roo.bootstrap.Popover.register(this);
19805 this.arrowEl = this.el.select('.arrow',true).first();
19806 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19807 this.el.enableDisplayMode('block');
19811 if (this.over === false && !this.parent()) {
19814 if (this.triggers === false) {
19819 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19820 var triggers = this.trigger ? this.trigger.split(' ') : [];
19821 Roo.each(triggers, function(trigger) {
19823 if (trigger == 'click') {
19824 on_el.on('click', this.toggle, this);
19825 } else if (trigger != 'manual') {
19826 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19827 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19829 on_el.on(eventIn ,this.enter, this);
19830 on_el.on(eventOut, this.leave, this);
19840 toggle : function () {
19841 this.hoverState == 'in' ? this.leave() : this.enter();
19844 enter : function () {
19846 clearTimeout(this.timeout);
19848 this.hoverState = 'in';
19850 if (!this.delay || !this.delay.show) {
19855 this.timeout = setTimeout(function () {
19856 if (_t.hoverState == 'in') {
19859 }, this.delay.show)
19862 leave : function() {
19863 clearTimeout(this.timeout);
19865 this.hoverState = 'out';
19867 if (!this.delay || !this.delay.hide) {
19872 this.timeout = setTimeout(function () {
19873 if (_t.hoverState == 'out') {
19876 }, this.delay.hide)
19880 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19881 * @param {string} (left|right|top|bottom) position
19883 show : function (on_el, placement)
19885 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19886 on_el = on_el || false; // default to false
19889 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19890 on_el = this.parent().el;
19891 } else if (this.over) {
19892 Roo.get(this.over);
19897 this.alignEl = Roo.get( on_el );
19900 this.render(document.body);
19906 if (this.title === false) {
19907 this.headerEl.hide();
19912 this.el.dom.style.display = 'block';
19915 if (this.alignEl) {
19916 this.updatePosition(this.placement, true);
19919 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19920 var es = this.el.getSize();
19921 var x = Roo.lib.Dom.getViewWidth()/2;
19922 var y = Roo.lib.Dom.getViewHeight()/2;
19923 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19928 //var arrow = this.el.select('.arrow',true).first();
19929 //arrow.set(align[2],
19931 this.el.addClass('in');
19935 this.hoverState = 'in';
19938 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19939 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19940 this.maskEl.dom.style.display = 'block';
19941 this.maskEl.addClass('show');
19943 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19945 this.fireEvent('show', this);
19949 * fire this manually after loading a grid in the table for example
19950 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19951 * @param {Boolean} try and move it if we cant get right position.
19953 updatePosition : function(placement, try_move)
19955 // allow for calling with no parameters
19956 placement = placement ? placement : this.placement;
19957 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19959 this.el.removeClass([
19960 'fade','top','bottom', 'left', 'right','in',
19961 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19963 this.el.addClass(placement + ' bs-popover-' + placement);
19965 if (!this.alignEl ) {
19969 switch (placement) {
19971 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19972 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19973 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19974 //normal display... or moved up/down.
19975 this.el.setXY(offset);
19976 var xy = this.alignEl.getAnchorXY('tr', false);
19978 this.arrowEl.setXY(xy);
19981 // continue through...
19982 return this.updatePosition('left', false);
19986 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19987 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19988 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19989 //normal display... or moved up/down.
19990 this.el.setXY(offset);
19991 var xy = this.alignEl.getAnchorXY('tl', false);
19992 xy[0]-=10;xy[1]+=5; // << fix me
19993 this.arrowEl.setXY(xy);
19997 return this.updatePosition('right', false);
20000 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20001 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20002 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20003 //normal display... or moved up/down.
20004 this.el.setXY(offset);
20005 var xy = this.alignEl.getAnchorXY('t', false);
20006 xy[1]-=10; // << fix me
20007 this.arrowEl.setXY(xy);
20011 return this.updatePosition('bottom', false);
20014 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20015 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20016 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20017 //normal display... or moved up/down.
20018 this.el.setXY(offset);
20019 var xy = this.alignEl.getAnchorXY('b', false);
20020 xy[1]+=2; // << fix me
20021 this.arrowEl.setXY(xy);
20025 return this.updatePosition('top', false);
20036 this.el.setXY([0,0]);
20037 this.el.removeClass('in');
20039 this.hoverState = null;
20040 this.maskEl.hide(); // always..
20041 this.fireEvent('hide', this);
20047 Roo.apply(Roo.bootstrap.Popover, {
20050 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20051 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20052 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20053 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20058 clickHander : false,
20061 onMouseDown : function(e)
20063 if (!e.getTarget(".roo-popover")) {
20071 register : function(popup)
20073 if (!Roo.bootstrap.Popover.clickHandler) {
20074 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20076 // hide other popups.
20078 this.popups.push(popup);
20080 hideAll : function()
20082 this.popups.forEach(function(p) {
20090 * Card header - holder for the card header elements.
20095 * @class Roo.bootstrap.PopoverNav
20096 * @extends Roo.bootstrap.NavGroup
20097 * Bootstrap Popover header navigation class
20099 * Create a new Popover Header Navigation
20100 * @param {Object} config The config object
20103 Roo.bootstrap.PopoverNav = function(config){
20104 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20107 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20110 container_method : 'getPopoverHeader'
20128 * @class Roo.bootstrap.Progress
20129 * @extends Roo.bootstrap.Component
20130 * Bootstrap Progress class
20131 * @cfg {Boolean} striped striped of the progress bar
20132 * @cfg {Boolean} active animated of the progress bar
20136 * Create a new Progress
20137 * @param {Object} config The config object
20140 Roo.bootstrap.Progress = function(config){
20141 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20144 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20149 getAutoCreate : function(){
20157 cfg.cls += ' progress-striped';
20161 cfg.cls += ' active';
20180 * @class Roo.bootstrap.ProgressBar
20181 * @extends Roo.bootstrap.Component
20182 * Bootstrap ProgressBar class
20183 * @cfg {Number} aria_valuenow aria-value now
20184 * @cfg {Number} aria_valuemin aria-value min
20185 * @cfg {Number} aria_valuemax aria-value max
20186 * @cfg {String} label label for the progress bar
20187 * @cfg {String} panel (success | info | warning | danger )
20188 * @cfg {String} role role of the progress bar
20189 * @cfg {String} sr_only text
20193 * Create a new ProgressBar
20194 * @param {Object} config The config object
20197 Roo.bootstrap.ProgressBar = function(config){
20198 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20201 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20205 aria_valuemax : 100,
20211 getAutoCreate : function()
20216 cls: 'progress-bar',
20217 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20229 cfg.role = this.role;
20232 if(this.aria_valuenow){
20233 cfg['aria-valuenow'] = this.aria_valuenow;
20236 if(this.aria_valuemin){
20237 cfg['aria-valuemin'] = this.aria_valuemin;
20240 if(this.aria_valuemax){
20241 cfg['aria-valuemax'] = this.aria_valuemax;
20244 if(this.label && !this.sr_only){
20245 cfg.html = this.label;
20249 cfg.cls += ' progress-bar-' + this.panel;
20255 update : function(aria_valuenow)
20257 this.aria_valuenow = aria_valuenow;
20259 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20274 * @class Roo.bootstrap.TabGroup
20275 * @extends Roo.bootstrap.Column
20276 * Bootstrap Column class
20277 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20278 * @cfg {Boolean} carousel true to make the group behave like a carousel
20279 * @cfg {Boolean} bullets show bullets for the panels
20280 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20281 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20282 * @cfg {Boolean} showarrow (true|false) show arrow default true
20285 * Create a new TabGroup
20286 * @param {Object} config The config object
20289 Roo.bootstrap.TabGroup = function(config){
20290 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20292 this.navId = Roo.id();
20295 Roo.bootstrap.TabGroup.register(this);
20299 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20302 transition : false,
20307 slideOnTouch : false,
20310 getAutoCreate : function()
20312 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20314 cfg.cls += ' tab-content';
20316 if (this.carousel) {
20317 cfg.cls += ' carousel slide';
20320 cls : 'carousel-inner',
20324 if(this.bullets && !Roo.isTouch){
20327 cls : 'carousel-bullets',
20331 if(this.bullets_cls){
20332 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20339 cfg.cn[0].cn.push(bullets);
20342 if(this.showarrow){
20343 cfg.cn[0].cn.push({
20345 class : 'carousel-arrow',
20349 class : 'carousel-prev',
20353 class : 'fa fa-chevron-left'
20359 class : 'carousel-next',
20363 class : 'fa fa-chevron-right'
20376 initEvents: function()
20378 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20379 // this.el.on("touchstart", this.onTouchStart, this);
20382 if(this.autoslide){
20385 this.slideFn = window.setInterval(function() {
20386 _this.showPanelNext();
20390 if(this.showarrow){
20391 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20392 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20398 // onTouchStart : function(e, el, o)
20400 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20404 // this.showPanelNext();
20408 getChildContainer : function()
20410 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20414 * register a Navigation item
20415 * @param {Roo.bootstrap.NavItem} the navitem to add
20417 register : function(item)
20419 this.tabs.push( item);
20420 item.navId = this.navId; // not really needed..
20425 getActivePanel : function()
20428 Roo.each(this.tabs, function(t) {
20438 getPanelByName : function(n)
20441 Roo.each(this.tabs, function(t) {
20442 if (t.tabId == n) {
20450 indexOfPanel : function(p)
20453 Roo.each(this.tabs, function(t,i) {
20454 if (t.tabId == p.tabId) {
20463 * show a specific panel
20464 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20465 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20467 showPanel : function (pan)
20469 if(this.transition || typeof(pan) == 'undefined'){
20470 Roo.log("waiting for the transitionend");
20474 if (typeof(pan) == 'number') {
20475 pan = this.tabs[pan];
20478 if (typeof(pan) == 'string') {
20479 pan = this.getPanelByName(pan);
20482 var cur = this.getActivePanel();
20485 Roo.log('pan or acitve pan is undefined');
20489 if (pan.tabId == this.getActivePanel().tabId) {
20493 if (false === cur.fireEvent('beforedeactivate')) {
20497 if(this.bullets > 0 && !Roo.isTouch){
20498 this.setActiveBullet(this.indexOfPanel(pan));
20501 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20503 //class="carousel-item carousel-item-next carousel-item-left"
20505 this.transition = true;
20506 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20507 var lr = dir == 'next' ? 'left' : 'right';
20508 pan.el.addClass(dir); // or prev
20509 pan.el.addClass('carousel-item-' + dir); // or prev
20510 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20511 cur.el.addClass(lr); // or right
20512 pan.el.addClass(lr);
20513 cur.el.addClass('carousel-item-' +lr); // or right
20514 pan.el.addClass('carousel-item-' +lr);
20518 cur.el.on('transitionend', function() {
20519 Roo.log("trans end?");
20521 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20522 pan.setActive(true);
20524 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20525 cur.setActive(false);
20527 _this.transition = false;
20529 }, this, { single: true } );
20534 cur.setActive(false);
20535 pan.setActive(true);
20540 showPanelNext : function()
20542 var i = this.indexOfPanel(this.getActivePanel());
20544 if (i >= this.tabs.length - 1 && !this.autoslide) {
20548 if (i >= this.tabs.length - 1 && this.autoslide) {
20552 this.showPanel(this.tabs[i+1]);
20555 showPanelPrev : function()
20557 var i = this.indexOfPanel(this.getActivePanel());
20559 if (i < 1 && !this.autoslide) {
20563 if (i < 1 && this.autoslide) {
20564 i = this.tabs.length;
20567 this.showPanel(this.tabs[i-1]);
20571 addBullet: function()
20573 if(!this.bullets || Roo.isTouch){
20576 var ctr = this.el.select('.carousel-bullets',true).first();
20577 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20578 var bullet = ctr.createChild({
20579 cls : 'bullet bullet-' + i
20580 },ctr.dom.lastChild);
20585 bullet.on('click', (function(e, el, o, ii, t){
20587 e.preventDefault();
20589 this.showPanel(ii);
20591 if(this.autoslide && this.slideFn){
20592 clearInterval(this.slideFn);
20593 this.slideFn = window.setInterval(function() {
20594 _this.showPanelNext();
20598 }).createDelegate(this, [i, bullet], true));
20603 setActiveBullet : function(i)
20609 Roo.each(this.el.select('.bullet', true).elements, function(el){
20610 el.removeClass('selected');
20613 var bullet = this.el.select('.bullet-' + i, true).first();
20619 bullet.addClass('selected');
20630 Roo.apply(Roo.bootstrap.TabGroup, {
20634 * register a Navigation Group
20635 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20637 register : function(navgrp)
20639 this.groups[navgrp.navId] = navgrp;
20643 * fetch a Navigation Group based on the navigation ID
20644 * if one does not exist , it will get created.
20645 * @param {string} the navgroup to add
20646 * @returns {Roo.bootstrap.NavGroup} the navgroup
20648 get: function(navId) {
20649 if (typeof(this.groups[navId]) == 'undefined') {
20650 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20652 return this.groups[navId] ;
20667 * @class Roo.bootstrap.TabPanel
20668 * @extends Roo.bootstrap.Component
20669 * Bootstrap TabPanel class
20670 * @cfg {Boolean} active panel active
20671 * @cfg {String} html panel content
20672 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20673 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20674 * @cfg {String} href click to link..
20675 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20679 * Create a new TabPanel
20680 * @param {Object} config The config object
20683 Roo.bootstrap.TabPanel = function(config){
20684 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20688 * Fires when the active status changes
20689 * @param {Roo.bootstrap.TabPanel} this
20690 * @param {Boolean} state the new state
20695 * @event beforedeactivate
20696 * Fires before a tab is de-activated - can be used to do validation on a form.
20697 * @param {Roo.bootstrap.TabPanel} this
20698 * @return {Boolean} false if there is an error
20701 'beforedeactivate': true
20704 this.tabId = this.tabId || Roo.id();
20708 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20715 touchSlide : false,
20716 getAutoCreate : function(){
20721 // item is needed for carousel - not sure if it has any effect otherwise
20722 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20723 html: this.html || ''
20727 cfg.cls += ' active';
20731 cfg.tabId = this.tabId;
20739 initEvents: function()
20741 var p = this.parent();
20743 this.navId = this.navId || p.navId;
20745 if (typeof(this.navId) != 'undefined') {
20746 // not really needed.. but just in case.. parent should be a NavGroup.
20747 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20751 var i = tg.tabs.length - 1;
20753 if(this.active && tg.bullets > 0 && i < tg.bullets){
20754 tg.setActiveBullet(i);
20758 this.el.on('click', this.onClick, this);
20760 if(Roo.isTouch && this.touchSlide){
20761 this.el.on("touchstart", this.onTouchStart, this);
20762 this.el.on("touchmove", this.onTouchMove, this);
20763 this.el.on("touchend", this.onTouchEnd, this);
20768 onRender : function(ct, position)
20770 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20773 setActive : function(state)
20775 Roo.log("panel - set active " + this.tabId + "=" + state);
20777 this.active = state;
20779 this.el.removeClass('active');
20781 } else if (!this.el.hasClass('active')) {
20782 this.el.addClass('active');
20785 this.fireEvent('changed', this, state);
20788 onClick : function(e)
20790 e.preventDefault();
20792 if(!this.href.length){
20796 window.location.href = this.href;
20805 onTouchStart : function(e)
20807 this.swiping = false;
20809 this.startX = e.browserEvent.touches[0].clientX;
20810 this.startY = e.browserEvent.touches[0].clientY;
20813 onTouchMove : function(e)
20815 this.swiping = true;
20817 this.endX = e.browserEvent.touches[0].clientX;
20818 this.endY = e.browserEvent.touches[0].clientY;
20821 onTouchEnd : function(e)
20828 var tabGroup = this.parent();
20830 if(this.endX > this.startX){ // swiping right
20831 tabGroup.showPanelPrev();
20835 if(this.startX > this.endX){ // swiping left
20836 tabGroup.showPanelNext();
20855 * @class Roo.bootstrap.DateField
20856 * @extends Roo.bootstrap.Input
20857 * Bootstrap DateField class
20858 * @cfg {Number} weekStart default 0
20859 * @cfg {String} viewMode default empty, (months|years)
20860 * @cfg {String} minViewMode default empty, (months|years)
20861 * @cfg {Number} startDate default -Infinity
20862 * @cfg {Number} endDate default Infinity
20863 * @cfg {Boolean} todayHighlight default false
20864 * @cfg {Boolean} todayBtn default false
20865 * @cfg {Boolean} calendarWeeks default false
20866 * @cfg {Object} daysOfWeekDisabled default empty
20867 * @cfg {Boolean} singleMode default false (true | false)
20869 * @cfg {Boolean} keyboardNavigation default true
20870 * @cfg {String} language default en
20873 * Create a new DateField
20874 * @param {Object} config The config object
20877 Roo.bootstrap.DateField = function(config){
20878 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20882 * Fires when this field show.
20883 * @param {Roo.bootstrap.DateField} this
20884 * @param {Mixed} date The date value
20889 * Fires when this field hide.
20890 * @param {Roo.bootstrap.DateField} this
20891 * @param {Mixed} date The date value
20896 * Fires when select a date.
20897 * @param {Roo.bootstrap.DateField} this
20898 * @param {Mixed} date The date value
20902 * @event beforeselect
20903 * Fires when before select a date.
20904 * @param {Roo.bootstrap.DateField} this
20905 * @param {Mixed} date The date value
20907 beforeselect : true
20911 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20914 * @cfg {String} format
20915 * The default date format string which can be overriden for localization support. The format must be
20916 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20920 * @cfg {String} altFormats
20921 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20922 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20924 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20932 todayHighlight : false,
20938 keyboardNavigation: true,
20940 calendarWeeks: false,
20942 startDate: -Infinity,
20946 daysOfWeekDisabled: [],
20950 singleMode : false,
20952 UTCDate: function()
20954 return new Date(Date.UTC.apply(Date, arguments));
20957 UTCToday: function()
20959 var today = new Date();
20960 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20963 getDate: function() {
20964 var d = this.getUTCDate();
20965 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20968 getUTCDate: function() {
20972 setDate: function(d) {
20973 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20976 setUTCDate: function(d) {
20978 this.setValue(this.formatDate(this.date));
20981 onRender: function(ct, position)
20984 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20986 this.language = this.language || 'en';
20987 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20988 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20990 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20991 this.format = this.format || 'm/d/y';
20992 this.isInline = false;
20993 this.isInput = true;
20994 this.component = this.el.select('.add-on', true).first() || false;
20995 this.component = (this.component && this.component.length === 0) ? false : this.component;
20996 this.hasInput = this.component && this.inputEl().length;
20998 if (typeof(this.minViewMode === 'string')) {
20999 switch (this.minViewMode) {
21001 this.minViewMode = 1;
21004 this.minViewMode = 2;
21007 this.minViewMode = 0;
21012 if (typeof(this.viewMode === 'string')) {
21013 switch (this.viewMode) {
21026 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21028 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21030 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21032 this.picker().on('mousedown', this.onMousedown, this);
21033 this.picker().on('click', this.onClick, this);
21035 this.picker().addClass('datepicker-dropdown');
21037 this.startViewMode = this.viewMode;
21039 if(this.singleMode){
21040 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21041 v.setVisibilityMode(Roo.Element.DISPLAY);
21045 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21046 v.setStyle('width', '189px');
21050 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21051 if(!this.calendarWeeks){
21056 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21057 v.attr('colspan', function(i, val){
21058 return parseInt(val) + 1;
21063 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21065 this.setStartDate(this.startDate);
21066 this.setEndDate(this.endDate);
21068 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21075 if(this.isInline) {
21080 picker : function()
21082 return this.pickerEl;
21083 // return this.el.select('.datepicker', true).first();
21086 fillDow: function()
21088 var dowCnt = this.weekStart;
21097 if(this.calendarWeeks){
21105 while (dowCnt < this.weekStart + 7) {
21109 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21113 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21116 fillMonths: function()
21119 var months = this.picker().select('>.datepicker-months td', true).first();
21121 months.dom.innerHTML = '';
21127 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21130 months.createChild(month);
21137 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;
21139 if (this.date < this.startDate) {
21140 this.viewDate = new Date(this.startDate);
21141 } else if (this.date > this.endDate) {
21142 this.viewDate = new Date(this.endDate);
21144 this.viewDate = new Date(this.date);
21152 var d = new Date(this.viewDate),
21153 year = d.getUTCFullYear(),
21154 month = d.getUTCMonth(),
21155 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21156 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21157 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21158 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21159 currentDate = this.date && this.date.valueOf(),
21160 today = this.UTCToday();
21162 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21164 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21166 // this.picker.select('>tfoot th.today').
21167 // .text(dates[this.language].today)
21168 // .toggle(this.todayBtn !== false);
21170 this.updateNavArrows();
21173 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21175 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21177 prevMonth.setUTCDate(day);
21179 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21181 var nextMonth = new Date(prevMonth);
21183 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21185 nextMonth = nextMonth.valueOf();
21187 var fillMonths = false;
21189 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21191 while(prevMonth.valueOf() <= nextMonth) {
21194 if (prevMonth.getUTCDay() === this.weekStart) {
21196 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21204 if(this.calendarWeeks){
21205 // ISO 8601: First week contains first thursday.
21206 // ISO also states week starts on Monday, but we can be more abstract here.
21208 // Start of current week: based on weekstart/current date
21209 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21210 // Thursday of this week
21211 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21212 // First Thursday of year, year from thursday
21213 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21214 // Calendar week: ms between thursdays, div ms per day, div 7 days
21215 calWeek = (th - yth) / 864e5 / 7 + 1;
21217 fillMonths.cn.push({
21225 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21227 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21230 if (this.todayHighlight &&
21231 prevMonth.getUTCFullYear() == today.getFullYear() &&
21232 prevMonth.getUTCMonth() == today.getMonth() &&
21233 prevMonth.getUTCDate() == today.getDate()) {
21234 clsName += ' today';
21237 if (currentDate && prevMonth.valueOf() === currentDate) {
21238 clsName += ' active';
21241 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21242 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21243 clsName += ' disabled';
21246 fillMonths.cn.push({
21248 cls: 'day ' + clsName,
21249 html: prevMonth.getDate()
21252 prevMonth.setDate(prevMonth.getDate()+1);
21255 var currentYear = this.date && this.date.getUTCFullYear();
21256 var currentMonth = this.date && this.date.getUTCMonth();
21258 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21260 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21261 v.removeClass('active');
21263 if(currentYear === year && k === currentMonth){
21264 v.addClass('active');
21267 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21268 v.addClass('disabled');
21274 year = parseInt(year/10, 10) * 10;
21276 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21278 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21281 for (var i = -1; i < 11; i++) {
21282 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21284 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21292 showMode: function(dir)
21295 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21298 Roo.each(this.picker().select('>div',true).elements, function(v){
21299 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21302 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21307 if(this.isInline) {
21311 this.picker().removeClass(['bottom', 'top']);
21313 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21315 * place to the top of element!
21319 this.picker().addClass('top');
21320 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21325 this.picker().addClass('bottom');
21327 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21330 parseDate : function(value)
21332 if(!value || value instanceof Date){
21335 var v = Date.parseDate(value, this.format);
21336 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21337 v = Date.parseDate(value, 'Y-m-d');
21339 if(!v && this.altFormats){
21340 if(!this.altFormatsArray){
21341 this.altFormatsArray = this.altFormats.split("|");
21343 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21344 v = Date.parseDate(value, this.altFormatsArray[i]);
21350 formatDate : function(date, fmt)
21352 return (!date || !(date instanceof Date)) ?
21353 date : date.dateFormat(fmt || this.format);
21356 onFocus : function()
21358 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21362 onBlur : function()
21364 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21366 var d = this.inputEl().getValue();
21373 showPopup : function()
21375 this.picker().show();
21379 this.fireEvent('showpopup', this, this.date);
21382 hidePopup : function()
21384 if(this.isInline) {
21387 this.picker().hide();
21388 this.viewMode = this.startViewMode;
21391 this.fireEvent('hidepopup', this, this.date);
21395 onMousedown: function(e)
21397 e.stopPropagation();
21398 e.preventDefault();
21403 Roo.bootstrap.DateField.superclass.keyup.call(this);
21407 setValue: function(v)
21409 if(this.fireEvent('beforeselect', this, v) !== false){
21410 var d = new Date(this.parseDate(v) ).clearTime();
21412 if(isNaN(d.getTime())){
21413 this.date = this.viewDate = '';
21414 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21418 v = this.formatDate(d);
21420 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21422 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21426 this.fireEvent('select', this, this.date);
21430 getValue: function()
21432 return this.formatDate(this.date);
21435 fireKey: function(e)
21437 if (!this.picker().isVisible()){
21438 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21444 var dateChanged = false,
21446 newDate, newViewDate;
21451 e.preventDefault();
21455 if (!this.keyboardNavigation) {
21458 dir = e.keyCode == 37 ? -1 : 1;
21461 newDate = this.moveYear(this.date, dir);
21462 newViewDate = this.moveYear(this.viewDate, dir);
21463 } else if (e.shiftKey){
21464 newDate = this.moveMonth(this.date, dir);
21465 newViewDate = this.moveMonth(this.viewDate, dir);
21467 newDate = new Date(this.date);
21468 newDate.setUTCDate(this.date.getUTCDate() + dir);
21469 newViewDate = new Date(this.viewDate);
21470 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21472 if (this.dateWithinRange(newDate)){
21473 this.date = newDate;
21474 this.viewDate = newViewDate;
21475 this.setValue(this.formatDate(this.date));
21477 e.preventDefault();
21478 dateChanged = true;
21483 if (!this.keyboardNavigation) {
21486 dir = e.keyCode == 38 ? -1 : 1;
21488 newDate = this.moveYear(this.date, dir);
21489 newViewDate = this.moveYear(this.viewDate, dir);
21490 } else if (e.shiftKey){
21491 newDate = this.moveMonth(this.date, dir);
21492 newViewDate = this.moveMonth(this.viewDate, dir);
21494 newDate = new Date(this.date);
21495 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21496 newViewDate = new Date(this.viewDate);
21497 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21499 if (this.dateWithinRange(newDate)){
21500 this.date = newDate;
21501 this.viewDate = newViewDate;
21502 this.setValue(this.formatDate(this.date));
21504 e.preventDefault();
21505 dateChanged = true;
21509 this.setValue(this.formatDate(this.date));
21511 e.preventDefault();
21514 this.setValue(this.formatDate(this.date));
21528 onClick: function(e)
21530 e.stopPropagation();
21531 e.preventDefault();
21533 var target = e.getTarget();
21535 if(target.nodeName.toLowerCase() === 'i'){
21536 target = Roo.get(target).dom.parentNode;
21539 var nodeName = target.nodeName;
21540 var className = target.className;
21541 var html = target.innerHTML;
21542 //Roo.log(nodeName);
21544 switch(nodeName.toLowerCase()) {
21546 switch(className) {
21552 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21553 switch(this.viewMode){
21555 this.viewDate = this.moveMonth(this.viewDate, dir);
21559 this.viewDate = this.moveYear(this.viewDate, dir);
21565 var date = new Date();
21566 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21568 this.setValue(this.formatDate(this.date));
21575 if (className.indexOf('disabled') < 0) {
21576 this.viewDate.setUTCDate(1);
21577 if (className.indexOf('month') > -1) {
21578 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21580 var year = parseInt(html, 10) || 0;
21581 this.viewDate.setUTCFullYear(year);
21585 if(this.singleMode){
21586 this.setValue(this.formatDate(this.viewDate));
21597 //Roo.log(className);
21598 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21599 var day = parseInt(html, 10) || 1;
21600 var year = (this.viewDate || new Date()).getUTCFullYear(),
21601 month = (this.viewDate || new Date()).getUTCMonth();
21603 if (className.indexOf('old') > -1) {
21610 } else if (className.indexOf('new') > -1) {
21618 //Roo.log([year,month,day]);
21619 this.date = this.UTCDate(year, month, day,0,0,0,0);
21620 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21622 //Roo.log(this.formatDate(this.date));
21623 this.setValue(this.formatDate(this.date));
21630 setStartDate: function(startDate)
21632 this.startDate = startDate || -Infinity;
21633 if (this.startDate !== -Infinity) {
21634 this.startDate = this.parseDate(this.startDate);
21637 this.updateNavArrows();
21640 setEndDate: function(endDate)
21642 this.endDate = endDate || Infinity;
21643 if (this.endDate !== Infinity) {
21644 this.endDate = this.parseDate(this.endDate);
21647 this.updateNavArrows();
21650 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21652 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21653 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21654 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21656 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21657 return parseInt(d, 10);
21660 this.updateNavArrows();
21663 updateNavArrows: function()
21665 if(this.singleMode){
21669 var d = new Date(this.viewDate),
21670 year = d.getUTCFullYear(),
21671 month = d.getUTCMonth();
21673 Roo.each(this.picker().select('.prev', true).elements, function(v){
21675 switch (this.viewMode) {
21678 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21684 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21691 Roo.each(this.picker().select('.next', true).elements, function(v){
21693 switch (this.viewMode) {
21696 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21702 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21710 moveMonth: function(date, dir)
21715 var new_date = new Date(date.valueOf()),
21716 day = new_date.getUTCDate(),
21717 month = new_date.getUTCMonth(),
21718 mag = Math.abs(dir),
21720 dir = dir > 0 ? 1 : -1;
21723 // If going back one month, make sure month is not current month
21724 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21726 return new_date.getUTCMonth() == month;
21728 // If going forward one month, make sure month is as expected
21729 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21731 return new_date.getUTCMonth() != new_month;
21733 new_month = month + dir;
21734 new_date.setUTCMonth(new_month);
21735 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21736 if (new_month < 0 || new_month > 11) {
21737 new_month = (new_month + 12) % 12;
21740 // For magnitudes >1, move one month at a time...
21741 for (var i=0; i<mag; i++) {
21742 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21743 new_date = this.moveMonth(new_date, dir);
21745 // ...then reset the day, keeping it in the new month
21746 new_month = new_date.getUTCMonth();
21747 new_date.setUTCDate(day);
21749 return new_month != new_date.getUTCMonth();
21752 // Common date-resetting loop -- if date is beyond end of month, make it
21755 new_date.setUTCDate(--day);
21756 new_date.setUTCMonth(new_month);
21761 moveYear: function(date, dir)
21763 return this.moveMonth(date, dir*12);
21766 dateWithinRange: function(date)
21768 return date >= this.startDate && date <= this.endDate;
21774 this.picker().remove();
21777 validateValue : function(value)
21779 if(this.getVisibilityEl().hasClass('hidden')){
21783 if(value.length < 1) {
21784 if(this.allowBlank){
21790 if(value.length < this.minLength){
21793 if(value.length > this.maxLength){
21797 var vt = Roo.form.VTypes;
21798 if(!vt[this.vtype](value, this)){
21802 if(typeof this.validator == "function"){
21803 var msg = this.validator(value);
21809 if(this.regex && !this.regex.test(value)){
21813 if(typeof(this.parseDate(value)) == 'undefined'){
21817 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21821 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21831 this.date = this.viewDate = '';
21833 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21838 Roo.apply(Roo.bootstrap.DateField, {
21849 html: '<i class="fa fa-arrow-left"/>'
21859 html: '<i class="fa fa-arrow-right"/>'
21901 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21902 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21903 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21904 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21905 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21918 navFnc: 'FullYear',
21923 navFnc: 'FullYear',
21928 Roo.apply(Roo.bootstrap.DateField, {
21932 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21936 cls: 'datepicker-days',
21940 cls: 'table-condensed',
21942 Roo.bootstrap.DateField.head,
21946 Roo.bootstrap.DateField.footer
21953 cls: 'datepicker-months',
21957 cls: 'table-condensed',
21959 Roo.bootstrap.DateField.head,
21960 Roo.bootstrap.DateField.content,
21961 Roo.bootstrap.DateField.footer
21968 cls: 'datepicker-years',
21972 cls: 'table-condensed',
21974 Roo.bootstrap.DateField.head,
21975 Roo.bootstrap.DateField.content,
21976 Roo.bootstrap.DateField.footer
21995 * @class Roo.bootstrap.TimeField
21996 * @extends Roo.bootstrap.Input
21997 * Bootstrap DateField class
22001 * Create a new TimeField
22002 * @param {Object} config The config object
22005 Roo.bootstrap.TimeField = function(config){
22006 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22010 * Fires when this field show.
22011 * @param {Roo.bootstrap.DateField} thisthis
22012 * @param {Mixed} date The date value
22017 * Fires when this field hide.
22018 * @param {Roo.bootstrap.DateField} this
22019 * @param {Mixed} date The date value
22024 * Fires when select a date.
22025 * @param {Roo.bootstrap.DateField} this
22026 * @param {Mixed} date The date value
22032 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22035 * @cfg {String} format
22036 * The default time format string which can be overriden for localization support. The format must be
22037 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22041 getAutoCreate : function()
22043 this.after = '<i class="fa far fa-clock"></i>';
22044 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22048 onRender: function(ct, position)
22051 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22053 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22055 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22057 this.pop = this.picker().select('>.datepicker-time',true).first();
22058 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22060 this.picker().on('mousedown', this.onMousedown, this);
22061 this.picker().on('click', this.onClick, this);
22063 this.picker().addClass('datepicker-dropdown');
22068 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22069 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22070 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22071 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22072 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22073 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22077 fireKey: function(e){
22078 if (!this.picker().isVisible()){
22079 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22085 e.preventDefault();
22093 this.onTogglePeriod();
22096 this.onIncrementMinutes();
22099 this.onDecrementMinutes();
22108 onClick: function(e) {
22109 e.stopPropagation();
22110 e.preventDefault();
22113 picker : function()
22115 return this.pickerEl;
22118 fillTime: function()
22120 var time = this.pop.select('tbody', true).first();
22122 time.dom.innerHTML = '';
22137 cls: 'hours-up fa fas fa-chevron-up'
22157 cls: 'minutes-up fa fas fa-chevron-up'
22178 cls: 'timepicker-hour',
22193 cls: 'timepicker-minute',
22208 cls: 'btn btn-primary period',
22230 cls: 'hours-down fa fas fa-chevron-down'
22250 cls: 'minutes-down fa fas fa-chevron-down'
22268 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22275 var hours = this.time.getHours();
22276 var minutes = this.time.getMinutes();
22289 hours = hours - 12;
22293 hours = '0' + hours;
22297 minutes = '0' + minutes;
22300 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22301 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22302 this.pop.select('button', true).first().dom.innerHTML = period;
22308 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22310 var cls = ['bottom'];
22312 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22319 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22323 //this.picker().setXY(20000,20000);
22324 this.picker().addClass(cls.join('-'));
22328 Roo.each(cls, function(c){
22333 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22334 //_this.picker().setTop(_this.inputEl().getHeight());
22338 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22340 //_this.picker().setTop(0 - _this.picker().getHeight());
22345 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22349 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22357 onFocus : function()
22359 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22363 onBlur : function()
22365 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22371 this.picker().show();
22376 this.fireEvent('show', this, this.date);
22381 this.picker().hide();
22384 this.fireEvent('hide', this, this.date);
22387 setTime : function()
22390 this.setValue(this.time.format(this.format));
22392 this.fireEvent('select', this, this.date);
22397 onMousedown: function(e){
22398 e.stopPropagation();
22399 e.preventDefault();
22402 onIncrementHours: function()
22404 Roo.log('onIncrementHours');
22405 this.time = this.time.add(Date.HOUR, 1);
22410 onDecrementHours: function()
22412 Roo.log('onDecrementHours');
22413 this.time = this.time.add(Date.HOUR, -1);
22417 onIncrementMinutes: function()
22419 Roo.log('onIncrementMinutes');
22420 this.time = this.time.add(Date.MINUTE, 1);
22424 onDecrementMinutes: function()
22426 Roo.log('onDecrementMinutes');
22427 this.time = this.time.add(Date.MINUTE, -1);
22431 onTogglePeriod: function()
22433 Roo.log('onTogglePeriod');
22434 this.time = this.time.add(Date.HOUR, 12);
22442 Roo.apply(Roo.bootstrap.TimeField, {
22446 cls: 'datepicker dropdown-menu',
22450 cls: 'datepicker-time',
22454 cls: 'table-condensed',
22483 cls: 'btn btn-info ok',
22511 * @class Roo.bootstrap.MonthField
22512 * @extends Roo.bootstrap.Input
22513 * Bootstrap MonthField class
22515 * @cfg {String} language default en
22518 * Create a new MonthField
22519 * @param {Object} config The config object
22522 Roo.bootstrap.MonthField = function(config){
22523 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22528 * Fires when this field show.
22529 * @param {Roo.bootstrap.MonthField} this
22530 * @param {Mixed} date The date value
22535 * Fires when this field hide.
22536 * @param {Roo.bootstrap.MonthField} this
22537 * @param {Mixed} date The date value
22542 * Fires when select a date.
22543 * @param {Roo.bootstrap.MonthField} this
22544 * @param {String} oldvalue The old value
22545 * @param {String} newvalue The new value
22551 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22553 onRender: function(ct, position)
22556 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22558 this.language = this.language || 'en';
22559 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22560 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22562 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22563 this.isInline = false;
22564 this.isInput = true;
22565 this.component = this.el.select('.add-on', true).first() || false;
22566 this.component = (this.component && this.component.length === 0) ? false : this.component;
22567 this.hasInput = this.component && this.inputEL().length;
22569 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22571 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22573 this.picker().on('mousedown', this.onMousedown, this);
22574 this.picker().on('click', this.onClick, this);
22576 this.picker().addClass('datepicker-dropdown');
22578 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22579 v.setStyle('width', '189px');
22586 if(this.isInline) {
22592 setValue: function(v, suppressEvent)
22594 var o = this.getValue();
22596 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22600 if(suppressEvent !== true){
22601 this.fireEvent('select', this, o, v);
22606 getValue: function()
22611 onClick: function(e)
22613 e.stopPropagation();
22614 e.preventDefault();
22616 var target = e.getTarget();
22618 if(target.nodeName.toLowerCase() === 'i'){
22619 target = Roo.get(target).dom.parentNode;
22622 var nodeName = target.nodeName;
22623 var className = target.className;
22624 var html = target.innerHTML;
22626 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22630 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22632 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22638 picker : function()
22640 return this.pickerEl;
22643 fillMonths: function()
22646 var months = this.picker().select('>.datepicker-months td', true).first();
22648 months.dom.innerHTML = '';
22654 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22657 months.createChild(month);
22666 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22667 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22670 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22671 e.removeClass('active');
22673 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22674 e.addClass('active');
22681 if(this.isInline) {
22685 this.picker().removeClass(['bottom', 'top']);
22687 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22689 * place to the top of element!
22693 this.picker().addClass('top');
22694 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22699 this.picker().addClass('bottom');
22701 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22704 onFocus : function()
22706 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22710 onBlur : function()
22712 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22714 var d = this.inputEl().getValue();
22723 this.picker().show();
22724 this.picker().select('>.datepicker-months', true).first().show();
22728 this.fireEvent('show', this, this.date);
22733 if(this.isInline) {
22736 this.picker().hide();
22737 this.fireEvent('hide', this, this.date);
22741 onMousedown: function(e)
22743 e.stopPropagation();
22744 e.preventDefault();
22749 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22753 fireKey: function(e)
22755 if (!this.picker().isVisible()){
22756 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22767 e.preventDefault();
22771 dir = e.keyCode == 37 ? -1 : 1;
22773 this.vIndex = this.vIndex + dir;
22775 if(this.vIndex < 0){
22779 if(this.vIndex > 11){
22783 if(isNaN(this.vIndex)){
22787 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22793 dir = e.keyCode == 38 ? -1 : 1;
22795 this.vIndex = this.vIndex + dir * 4;
22797 if(this.vIndex < 0){
22801 if(this.vIndex > 11){
22805 if(isNaN(this.vIndex)){
22809 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22814 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22815 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22819 e.preventDefault();
22822 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22823 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22839 this.picker().remove();
22844 Roo.apply(Roo.bootstrap.MonthField, {
22863 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22864 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22869 Roo.apply(Roo.bootstrap.MonthField, {
22873 cls: 'datepicker dropdown-menu roo-dynamic',
22877 cls: 'datepicker-months',
22881 cls: 'table-condensed',
22883 Roo.bootstrap.DateField.content
22903 * @class Roo.bootstrap.CheckBox
22904 * @extends Roo.bootstrap.Input
22905 * Bootstrap CheckBox class
22907 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22908 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22909 * @cfg {String} boxLabel The text that appears beside the checkbox
22910 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22911 * @cfg {Boolean} checked initnal the element
22912 * @cfg {Boolean} inline inline the element (default false)
22913 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22914 * @cfg {String} tooltip label tooltip
22917 * Create a new CheckBox
22918 * @param {Object} config The config object
22921 Roo.bootstrap.CheckBox = function(config){
22922 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22927 * Fires when the element is checked or unchecked.
22928 * @param {Roo.bootstrap.CheckBox} this This input
22929 * @param {Boolean} checked The new checked value
22934 * Fires when the element is click.
22935 * @param {Roo.bootstrap.CheckBox} this This input
22942 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22944 inputType: 'checkbox',
22953 // checkbox success does not make any sense really..
22958 getAutoCreate : function()
22960 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22966 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22969 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22975 type : this.inputType,
22976 value : this.inputValue,
22977 cls : 'roo-' + this.inputType, //'form-box',
22978 placeholder : this.placeholder || ''
22982 if(this.inputType != 'radio'){
22986 cls : 'roo-hidden-value',
22987 value : this.checked ? this.inputValue : this.valueOff
22992 if (this.weight) { // Validity check?
22993 cfg.cls += " " + this.inputType + "-" + this.weight;
22996 if (this.disabled) {
22997 input.disabled=true;
23001 input.checked = this.checked;
23006 input.name = this.name;
23008 if(this.inputType != 'radio'){
23009 hidden.name = this.name;
23010 input.name = '_hidden_' + this.name;
23015 input.cls += ' input-' + this.size;
23020 ['xs','sm','md','lg'].map(function(size){
23021 if (settings[size]) {
23022 cfg.cls += ' col-' + size + '-' + settings[size];
23026 var inputblock = input;
23028 if (this.before || this.after) {
23031 cls : 'input-group',
23036 inputblock.cn.push({
23038 cls : 'input-group-addon',
23043 inputblock.cn.push(input);
23045 if(this.inputType != 'radio'){
23046 inputblock.cn.push(hidden);
23050 inputblock.cn.push({
23052 cls : 'input-group-addon',
23058 var boxLabelCfg = false;
23064 //'for': id, // box label is handled by onclick - so no for...
23066 html: this.boxLabel
23069 boxLabelCfg.tooltip = this.tooltip;
23075 if (align ==='left' && this.fieldLabel.length) {
23076 // Roo.log("left and has label");
23081 cls : 'control-label',
23082 html : this.fieldLabel
23093 cfg.cn[1].cn.push(boxLabelCfg);
23096 if(this.labelWidth > 12){
23097 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23100 if(this.labelWidth < 13 && this.labelmd == 0){
23101 this.labelmd = this.labelWidth;
23104 if(this.labellg > 0){
23105 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23106 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23109 if(this.labelmd > 0){
23110 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23111 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23114 if(this.labelsm > 0){
23115 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23116 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23119 if(this.labelxs > 0){
23120 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23121 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23124 } else if ( this.fieldLabel.length) {
23125 // Roo.log(" label");
23129 tag: this.boxLabel ? 'span' : 'label',
23131 cls: 'control-label box-input-label',
23132 //cls : 'input-group-addon',
23133 html : this.fieldLabel
23140 cfg.cn.push(boxLabelCfg);
23145 // Roo.log(" no label && no align");
23146 cfg.cn = [ inputblock ] ;
23148 cfg.cn.push(boxLabelCfg);
23156 if(this.inputType != 'radio'){
23157 cfg.cn.push(hidden);
23165 * return the real input element.
23167 inputEl: function ()
23169 return this.el.select('input.roo-' + this.inputType,true).first();
23171 hiddenEl: function ()
23173 return this.el.select('input.roo-hidden-value',true).first();
23176 labelEl: function()
23178 return this.el.select('label.control-label',true).first();
23180 /* depricated... */
23184 return this.labelEl();
23187 boxLabelEl: function()
23189 return this.el.select('label.box-label',true).first();
23192 initEvents : function()
23194 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23196 this.inputEl().on('click', this.onClick, this);
23198 if (this.boxLabel) {
23199 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23202 this.startValue = this.getValue();
23205 Roo.bootstrap.CheckBox.register(this);
23209 onClick : function(e)
23211 if(this.fireEvent('click', this, e) !== false){
23212 this.setChecked(!this.checked);
23217 setChecked : function(state,suppressEvent)
23219 this.startValue = this.getValue();
23221 if(this.inputType == 'radio'){
23223 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23224 e.dom.checked = false;
23227 this.inputEl().dom.checked = true;
23229 this.inputEl().dom.value = this.inputValue;
23231 if(suppressEvent !== true){
23232 this.fireEvent('check', this, true);
23240 this.checked = state;
23242 this.inputEl().dom.checked = state;
23245 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23247 if(suppressEvent !== true){
23248 this.fireEvent('check', this, state);
23254 getValue : function()
23256 if(this.inputType == 'radio'){
23257 return this.getGroupValue();
23260 return this.hiddenEl().dom.value;
23264 getGroupValue : function()
23266 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23270 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23273 setValue : function(v,suppressEvent)
23275 if(this.inputType == 'radio'){
23276 this.setGroupValue(v, suppressEvent);
23280 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23285 setGroupValue : function(v, suppressEvent)
23287 this.startValue = this.getValue();
23289 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23290 e.dom.checked = false;
23292 if(e.dom.value == v){
23293 e.dom.checked = true;
23297 if(suppressEvent !== true){
23298 this.fireEvent('check', this, true);
23306 validate : function()
23308 if(this.getVisibilityEl().hasClass('hidden')){
23314 (this.inputType == 'radio' && this.validateRadio()) ||
23315 (this.inputType == 'checkbox' && this.validateCheckbox())
23321 this.markInvalid();
23325 validateRadio : function()
23327 if(this.getVisibilityEl().hasClass('hidden')){
23331 if(this.allowBlank){
23337 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23338 if(!e.dom.checked){
23350 validateCheckbox : function()
23353 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23354 //return (this.getValue() == this.inputValue) ? true : false;
23357 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23365 for(var i in group){
23366 if(group[i].el.isVisible(true)){
23374 for(var i in group){
23379 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23386 * Mark this field as valid
23388 markValid : function()
23392 this.fireEvent('valid', this);
23394 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23397 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23404 if(this.inputType == 'radio'){
23405 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23406 var fg = e.findParent('.form-group', false, true);
23407 if (Roo.bootstrap.version == 3) {
23408 fg.removeClass([_this.invalidClass, _this.validClass]);
23409 fg.addClass(_this.validClass);
23411 fg.removeClass(['is-valid', 'is-invalid']);
23412 fg.addClass('is-valid');
23420 var fg = this.el.findParent('.form-group', false, true);
23421 if (Roo.bootstrap.version == 3) {
23422 fg.removeClass([this.invalidClass, this.validClass]);
23423 fg.addClass(this.validClass);
23425 fg.removeClass(['is-valid', 'is-invalid']);
23426 fg.addClass('is-valid');
23431 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23437 for(var i in group){
23438 var fg = group[i].el.findParent('.form-group', false, true);
23439 if (Roo.bootstrap.version == 3) {
23440 fg.removeClass([this.invalidClass, this.validClass]);
23441 fg.addClass(this.validClass);
23443 fg.removeClass(['is-valid', 'is-invalid']);
23444 fg.addClass('is-valid');
23450 * Mark this field as invalid
23451 * @param {String} msg The validation message
23453 markInvalid : function(msg)
23455 if(this.allowBlank){
23461 this.fireEvent('invalid', this, msg);
23463 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23466 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23470 label.markInvalid();
23473 if(this.inputType == 'radio'){
23475 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23476 var fg = e.findParent('.form-group', false, true);
23477 if (Roo.bootstrap.version == 3) {
23478 fg.removeClass([_this.invalidClass, _this.validClass]);
23479 fg.addClass(_this.invalidClass);
23481 fg.removeClass(['is-invalid', 'is-valid']);
23482 fg.addClass('is-invalid');
23490 var fg = this.el.findParent('.form-group', false, true);
23491 if (Roo.bootstrap.version == 3) {
23492 fg.removeClass([_this.invalidClass, _this.validClass]);
23493 fg.addClass(_this.invalidClass);
23495 fg.removeClass(['is-invalid', 'is-valid']);
23496 fg.addClass('is-invalid');
23501 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23507 for(var i in group){
23508 var fg = group[i].el.findParent('.form-group', false, true);
23509 if (Roo.bootstrap.version == 3) {
23510 fg.removeClass([_this.invalidClass, _this.validClass]);
23511 fg.addClass(_this.invalidClass);
23513 fg.removeClass(['is-invalid', 'is-valid']);
23514 fg.addClass('is-invalid');
23520 clearInvalid : function()
23522 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23524 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23526 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23528 if (label && label.iconEl) {
23529 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23530 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23534 disable : function()
23536 if(this.inputType != 'radio'){
23537 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23544 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23545 _this.getActionEl().addClass(this.disabledClass);
23546 e.dom.disabled = true;
23550 this.disabled = true;
23551 this.fireEvent("disable", this);
23555 enable : function()
23557 if(this.inputType != 'radio'){
23558 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23565 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23566 _this.getActionEl().removeClass(this.disabledClass);
23567 e.dom.disabled = false;
23571 this.disabled = false;
23572 this.fireEvent("enable", this);
23576 setBoxLabel : function(v)
23581 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23587 Roo.apply(Roo.bootstrap.CheckBox, {
23592 * register a CheckBox Group
23593 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23595 register : function(checkbox)
23597 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23598 this.groups[checkbox.groupId] = {};
23601 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23605 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23609 * fetch a CheckBox Group based on the group ID
23610 * @param {string} the group ID
23611 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23613 get: function(groupId) {
23614 if (typeof(this.groups[groupId]) == 'undefined') {
23618 return this.groups[groupId] ;
23631 * @class Roo.bootstrap.Radio
23632 * @extends Roo.bootstrap.Component
23633 * Bootstrap Radio class
23634 * @cfg {String} boxLabel - the label associated
23635 * @cfg {String} value - the value of radio
23638 * Create a new Radio
23639 * @param {Object} config The config object
23641 Roo.bootstrap.Radio = function(config){
23642 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23646 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23652 getAutoCreate : function()
23656 cls : 'form-group radio',
23661 html : this.boxLabel
23669 initEvents : function()
23671 this.parent().register(this);
23673 this.el.on('click', this.onClick, this);
23677 onClick : function(e)
23679 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23680 this.setChecked(true);
23684 setChecked : function(state, suppressEvent)
23686 this.parent().setValue(this.value, suppressEvent);
23690 setBoxLabel : function(v)
23695 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23710 * @class Roo.bootstrap.SecurePass
23711 * @extends Roo.bootstrap.Input
23712 * Bootstrap SecurePass class
23716 * Create a new SecurePass
23717 * @param {Object} config The config object
23720 Roo.bootstrap.SecurePass = function (config) {
23721 // these go here, so the translation tool can replace them..
23723 PwdEmpty: "Please type a password, and then retype it to confirm.",
23724 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23725 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23726 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23727 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23728 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23729 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23730 TooWeak: "Your password is Too Weak."
23732 this.meterLabel = "Password strength:";
23733 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23734 this.meterClass = [
23735 "roo-password-meter-tooweak",
23736 "roo-password-meter-weak",
23737 "roo-password-meter-medium",
23738 "roo-password-meter-strong",
23739 "roo-password-meter-grey"
23744 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23747 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23749 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23751 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23752 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23753 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23754 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23755 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23756 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23757 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23767 * @cfg {String/Object} Label for the strength meter (defaults to
23768 * 'Password strength:')
23773 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23774 * ['Weak', 'Medium', 'Strong'])
23777 pwdStrengths: false,
23790 initEvents: function ()
23792 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23794 if (this.el.is('input[type=password]') && Roo.isSafari) {
23795 this.el.on('keydown', this.SafariOnKeyDown, this);
23798 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23801 onRender: function (ct, position)
23803 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23804 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23805 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23807 this.trigger.createChild({
23812 cls: 'roo-password-meter-grey col-xs-12',
23815 //width: this.meterWidth + 'px'
23819 cls: 'roo-password-meter-text'
23825 if (this.hideTrigger) {
23826 this.trigger.setDisplayed(false);
23828 this.setSize(this.width || '', this.height || '');
23831 onDestroy: function ()
23833 if (this.trigger) {
23834 this.trigger.removeAllListeners();
23835 this.trigger.remove();
23838 this.wrap.remove();
23840 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23843 checkStrength: function ()
23845 var pwd = this.inputEl().getValue();
23846 if (pwd == this._lastPwd) {
23851 if (this.ClientSideStrongPassword(pwd)) {
23853 } else if (this.ClientSideMediumPassword(pwd)) {
23855 } else if (this.ClientSideWeakPassword(pwd)) {
23861 Roo.log('strength1: ' + strength);
23863 //var pm = this.trigger.child('div/div/div').dom;
23864 var pm = this.trigger.child('div/div');
23865 pm.removeClass(this.meterClass);
23866 pm.addClass(this.meterClass[strength]);
23869 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23871 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23873 this._lastPwd = pwd;
23877 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23879 this._lastPwd = '';
23881 var pm = this.trigger.child('div/div');
23882 pm.removeClass(this.meterClass);
23883 pm.addClass('roo-password-meter-grey');
23886 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23889 this.inputEl().dom.type='password';
23892 validateValue: function (value)
23894 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23897 if (value.length == 0) {
23898 if (this.allowBlank) {
23899 this.clearInvalid();
23903 this.markInvalid(this.errors.PwdEmpty);
23904 this.errorMsg = this.errors.PwdEmpty;
23912 if (!value.match(/[\x21-\x7e]+/)) {
23913 this.markInvalid(this.errors.PwdBadChar);
23914 this.errorMsg = this.errors.PwdBadChar;
23917 if (value.length < 6) {
23918 this.markInvalid(this.errors.PwdShort);
23919 this.errorMsg = this.errors.PwdShort;
23922 if (value.length > 16) {
23923 this.markInvalid(this.errors.PwdLong);
23924 this.errorMsg = this.errors.PwdLong;
23928 if (this.ClientSideStrongPassword(value)) {
23930 } else if (this.ClientSideMediumPassword(value)) {
23932 } else if (this.ClientSideWeakPassword(value)) {
23939 if (strength < 2) {
23940 //this.markInvalid(this.errors.TooWeak);
23941 this.errorMsg = this.errors.TooWeak;
23946 console.log('strength2: ' + strength);
23948 //var pm = this.trigger.child('div/div/div').dom;
23950 var pm = this.trigger.child('div/div');
23951 pm.removeClass(this.meterClass);
23952 pm.addClass(this.meterClass[strength]);
23954 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23956 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23958 this.errorMsg = '';
23962 CharacterSetChecks: function (type)
23965 this.fResult = false;
23968 isctype: function (character, type)
23971 case this.kCapitalLetter:
23972 if (character >= 'A' && character <= 'Z') {
23977 case this.kSmallLetter:
23978 if (character >= 'a' && character <= 'z') {
23984 if (character >= '0' && character <= '9') {
23989 case this.kPunctuation:
23990 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24001 IsLongEnough: function (pwd, size)
24003 return !(pwd == null || isNaN(size) || pwd.length < size);
24006 SpansEnoughCharacterSets: function (word, nb)
24008 if (!this.IsLongEnough(word, nb))
24013 var characterSetChecks = new Array(
24014 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24015 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24018 for (var index = 0; index < word.length; ++index) {
24019 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24020 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24021 characterSetChecks[nCharSet].fResult = true;
24028 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24029 if (characterSetChecks[nCharSet].fResult) {
24034 if (nCharSets < nb) {
24040 ClientSideStrongPassword: function (pwd)
24042 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24045 ClientSideMediumPassword: function (pwd)
24047 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24050 ClientSideWeakPassword: function (pwd)
24052 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24055 })//<script type="text/javascript">
24058 * Based Ext JS Library 1.1.1
24059 * Copyright(c) 2006-2007, Ext JS, LLC.
24065 * @class Roo.HtmlEditorCore
24066 * @extends Roo.Component
24067 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24069 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24072 Roo.HtmlEditorCore = function(config){
24075 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24080 * @event initialize
24081 * Fires when the editor is fully initialized (including the iframe)
24082 * @param {Roo.HtmlEditorCore} this
24087 * Fires when the editor is first receives the focus. Any insertion must wait
24088 * until after this event.
24089 * @param {Roo.HtmlEditorCore} this
24093 * @event beforesync
24094 * Fires before the textarea is updated with content from the editor iframe. Return false
24095 * to cancel the sync.
24096 * @param {Roo.HtmlEditorCore} this
24097 * @param {String} html
24101 * @event beforepush
24102 * Fires before the iframe editor is updated with content from the textarea. Return false
24103 * to cancel the push.
24104 * @param {Roo.HtmlEditorCore} this
24105 * @param {String} html
24110 * Fires when the textarea is updated with content from the editor iframe.
24111 * @param {Roo.HtmlEditorCore} this
24112 * @param {String} html
24117 * Fires when the iframe editor is updated with content from the textarea.
24118 * @param {Roo.HtmlEditorCore} this
24119 * @param {String} html
24124 * @event editorevent
24125 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24126 * @param {Roo.HtmlEditorCore} this
24132 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24134 // defaults : white / black...
24135 this.applyBlacklists();
24142 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24146 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24152 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24157 * @cfg {Number} height (in pixels)
24161 * @cfg {Number} width (in pixels)
24166 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24169 stylesheets: false,
24174 // private properties
24175 validationEvent : false,
24177 initialized : false,
24179 sourceEditMode : false,
24180 onFocus : Roo.emptyFn,
24182 hideMode:'offsets',
24186 // blacklist + whitelisted elements..
24193 * Protected method that will not generally be called directly. It
24194 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24195 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24197 getDocMarkup : function(){
24201 // inherit styels from page...??
24202 if (this.stylesheets === false) {
24204 Roo.get(document.head).select('style').each(function(node) {
24205 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24208 Roo.get(document.head).select('link').each(function(node) {
24209 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24212 } else if (!this.stylesheets.length) {
24214 st = '<style type="text/css">' +
24215 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24218 for (var i in this.stylesheets) {
24219 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24224 st += '<style type="text/css">' +
24225 'IMG { cursor: pointer } ' +
24228 var cls = 'roo-htmleditor-body';
24230 if(this.bodyCls.length){
24231 cls += ' ' + this.bodyCls;
24234 return '<html><head>' + st +
24235 //<style type="text/css">' +
24236 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24238 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24242 onRender : function(ct, position)
24245 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24246 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24249 this.el.dom.style.border = '0 none';
24250 this.el.dom.setAttribute('tabIndex', -1);
24251 this.el.addClass('x-hidden hide');
24255 if(Roo.isIE){ // fix IE 1px bogus margin
24256 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24260 this.frameId = Roo.id();
24264 var iframe = this.owner.wrap.createChild({
24266 cls: 'form-control', // bootstrap..
24268 name: this.frameId,
24269 frameBorder : 'no',
24270 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24275 this.iframe = iframe.dom;
24277 this.assignDocWin();
24279 this.doc.designMode = 'on';
24282 this.doc.write(this.getDocMarkup());
24286 var task = { // must defer to wait for browser to be ready
24288 //console.log("run task?" + this.doc.readyState);
24289 this.assignDocWin();
24290 if(this.doc.body || this.doc.readyState == 'complete'){
24292 this.doc.designMode="on";
24296 Roo.TaskMgr.stop(task);
24297 this.initEditor.defer(10, this);
24304 Roo.TaskMgr.start(task);
24309 onResize : function(w, h)
24311 Roo.log('resize: ' +w + ',' + h );
24312 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24316 if(typeof w == 'number'){
24318 this.iframe.style.width = w + 'px';
24320 if(typeof h == 'number'){
24322 this.iframe.style.height = h + 'px';
24324 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24331 * Toggles the editor between standard and source edit mode.
24332 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24334 toggleSourceEdit : function(sourceEditMode){
24336 this.sourceEditMode = sourceEditMode === true;
24338 if(this.sourceEditMode){
24340 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24343 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24344 //this.iframe.className = '';
24347 //this.setSize(this.owner.wrap.getSize());
24348 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24355 * Protected method that will not generally be called directly. If you need/want
24356 * custom HTML cleanup, this is the method you should override.
24357 * @param {String} html The HTML to be cleaned
24358 * return {String} The cleaned HTML
24360 cleanHtml : function(html){
24361 html = String(html);
24362 if(html.length > 5){
24363 if(Roo.isSafari){ // strip safari nonsense
24364 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24367 if(html == ' '){
24374 * HTML Editor -> Textarea
24375 * Protected method that will not generally be called directly. Syncs the contents
24376 * of the editor iframe with the textarea.
24378 syncValue : function(){
24379 if(this.initialized){
24380 var bd = (this.doc.body || this.doc.documentElement);
24381 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24382 var html = bd.innerHTML;
24384 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24385 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24387 html = '<div style="'+m[0]+'">' + html + '</div>';
24390 html = this.cleanHtml(html);
24391 // fix up the special chars.. normaly like back quotes in word...
24392 // however we do not want to do this with chinese..
24393 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24395 var cc = match.charCodeAt();
24397 // Get the character value, handling surrogate pairs
24398 if (match.length == 2) {
24399 // It's a surrogate pair, calculate the Unicode code point
24400 var high = match.charCodeAt(0) - 0xD800;
24401 var low = match.charCodeAt(1) - 0xDC00;
24402 cc = (high * 0x400) + low + 0x10000;
24404 (cc >= 0x4E00 && cc < 0xA000 ) ||
24405 (cc >= 0x3400 && cc < 0x4E00 ) ||
24406 (cc >= 0xf900 && cc < 0xfb00 )
24411 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24412 return "&#" + cc + ";";
24419 if(this.owner.fireEvent('beforesync', this, html) !== false){
24420 this.el.dom.value = html;
24421 this.owner.fireEvent('sync', this, html);
24427 * Protected method that will not generally be called directly. Pushes the value of the textarea
24428 * into the iframe editor.
24430 pushValue : function(){
24431 if(this.initialized){
24432 var v = this.el.dom.value.trim();
24434 // if(v.length < 1){
24438 if(this.owner.fireEvent('beforepush', this, v) !== false){
24439 var d = (this.doc.body || this.doc.documentElement);
24441 this.cleanUpPaste();
24442 this.el.dom.value = d.innerHTML;
24443 this.owner.fireEvent('push', this, v);
24449 deferFocus : function(){
24450 this.focus.defer(10, this);
24454 focus : function(){
24455 if(this.win && !this.sourceEditMode){
24462 assignDocWin: function()
24464 var iframe = this.iframe;
24467 this.doc = iframe.contentWindow.document;
24468 this.win = iframe.contentWindow;
24470 // if (!Roo.get(this.frameId)) {
24473 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24474 // this.win = Roo.get(this.frameId).dom.contentWindow;
24476 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24480 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24481 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24486 initEditor : function(){
24487 //console.log("INIT EDITOR");
24488 this.assignDocWin();
24492 this.doc.designMode="on";
24494 this.doc.write(this.getDocMarkup());
24497 var dbody = (this.doc.body || this.doc.documentElement);
24498 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24499 // this copies styles from the containing element into thsi one..
24500 // not sure why we need all of this..
24501 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24503 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24504 //ss['background-attachment'] = 'fixed'; // w3c
24505 dbody.bgProperties = 'fixed'; // ie
24506 //Roo.DomHelper.applyStyles(dbody, ss);
24507 Roo.EventManager.on(this.doc, {
24508 //'mousedown': this.onEditorEvent,
24509 'mouseup': this.onEditorEvent,
24510 'dblclick': this.onEditorEvent,
24511 'click': this.onEditorEvent,
24512 'keyup': this.onEditorEvent,
24517 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24519 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24520 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24522 this.initialized = true;
24524 this.owner.fireEvent('initialize', this);
24529 onDestroy : function(){
24535 //for (var i =0; i < this.toolbars.length;i++) {
24536 // // fixme - ask toolbars for heights?
24537 // this.toolbars[i].onDestroy();
24540 //this.wrap.dom.innerHTML = '';
24541 //this.wrap.remove();
24546 onFirstFocus : function(){
24548 this.assignDocWin();
24551 this.activated = true;
24554 if(Roo.isGecko){ // prevent silly gecko errors
24556 var s = this.win.getSelection();
24557 if(!s.focusNode || s.focusNode.nodeType != 3){
24558 var r = s.getRangeAt(0);
24559 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24564 this.execCmd('useCSS', true);
24565 this.execCmd('styleWithCSS', false);
24568 this.owner.fireEvent('activate', this);
24572 adjustFont: function(btn){
24573 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24574 //if(Roo.isSafari){ // safari
24577 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24578 if(Roo.isSafari){ // safari
24579 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24580 v = (v < 10) ? 10 : v;
24581 v = (v > 48) ? 48 : v;
24582 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24587 v = Math.max(1, v+adjust);
24589 this.execCmd('FontSize', v );
24592 onEditorEvent : function(e)
24594 this.owner.fireEvent('editorevent', this, e);
24595 // this.updateToolbar();
24596 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24599 insertTag : function(tg)
24601 // could be a bit smarter... -> wrap the current selected tRoo..
24602 if (tg.toLowerCase() == 'span' ||
24603 tg.toLowerCase() == 'code' ||
24604 tg.toLowerCase() == 'sup' ||
24605 tg.toLowerCase() == 'sub'
24608 range = this.createRange(this.getSelection());
24609 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24610 wrappingNode.appendChild(range.extractContents());
24611 range.insertNode(wrappingNode);
24618 this.execCmd("formatblock", tg);
24622 insertText : function(txt)
24626 var range = this.createRange();
24627 range.deleteContents();
24628 //alert(Sender.getAttribute('label'));
24630 range.insertNode(this.doc.createTextNode(txt));
24636 * Executes a Midas editor command on the editor document and performs necessary focus and
24637 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24638 * @param {String} cmd The Midas command
24639 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24641 relayCmd : function(cmd, value){
24643 this.execCmd(cmd, value);
24644 this.owner.fireEvent('editorevent', this);
24645 //this.updateToolbar();
24646 this.owner.deferFocus();
24650 * Executes a Midas editor command directly on the editor document.
24651 * For visual commands, you should use {@link #relayCmd} instead.
24652 * <b>This should only be called after the editor is initialized.</b>
24653 * @param {String} cmd The Midas command
24654 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24656 execCmd : function(cmd, value){
24657 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24664 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24666 * @param {String} text | dom node..
24668 insertAtCursor : function(text)
24671 if(!this.activated){
24677 var r = this.doc.selection.createRange();
24688 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24692 // from jquery ui (MIT licenced)
24694 var win = this.win;
24696 if (win.getSelection && win.getSelection().getRangeAt) {
24697 range = win.getSelection().getRangeAt(0);
24698 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24699 range.insertNode(node);
24700 } else if (win.document.selection && win.document.selection.createRange) {
24701 // no firefox support
24702 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24703 win.document.selection.createRange().pasteHTML(txt);
24705 // no firefox support
24706 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24707 this.execCmd('InsertHTML', txt);
24716 mozKeyPress : function(e){
24718 var c = e.getCharCode(), cmd;
24721 c = String.fromCharCode(c).toLowerCase();
24735 this.cleanUpPaste.defer(100, this);
24743 e.preventDefault();
24751 fixKeys : function(){ // load time branching for fastest keydown performance
24753 return function(e){
24754 var k = e.getKey(), r;
24757 r = this.doc.selection.createRange();
24760 r.pasteHTML('    ');
24767 r = this.doc.selection.createRange();
24769 var target = r.parentElement();
24770 if(!target || target.tagName.toLowerCase() != 'li'){
24772 r.pasteHTML('<br />');
24778 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24779 this.cleanUpPaste.defer(100, this);
24785 }else if(Roo.isOpera){
24786 return function(e){
24787 var k = e.getKey();
24791 this.execCmd('InsertHTML','    ');
24794 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24795 this.cleanUpPaste.defer(100, this);
24800 }else if(Roo.isSafari){
24801 return function(e){
24802 var k = e.getKey();
24806 this.execCmd('InsertText','\t');
24810 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24811 this.cleanUpPaste.defer(100, this);
24819 getAllAncestors: function()
24821 var p = this.getSelectedNode();
24824 a.push(p); // push blank onto stack..
24825 p = this.getParentElement();
24829 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24833 a.push(this.doc.body);
24837 lastSelNode : false,
24840 getSelection : function()
24842 this.assignDocWin();
24843 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24846 getSelectedNode: function()
24848 // this may only work on Gecko!!!
24850 // should we cache this!!!!
24855 var range = this.createRange(this.getSelection()).cloneRange();
24858 var parent = range.parentElement();
24860 var testRange = range.duplicate();
24861 testRange.moveToElementText(parent);
24862 if (testRange.inRange(range)) {
24865 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24868 parent = parent.parentElement;
24873 // is ancestor a text element.
24874 var ac = range.commonAncestorContainer;
24875 if (ac.nodeType == 3) {
24876 ac = ac.parentNode;
24879 var ar = ac.childNodes;
24882 var other_nodes = [];
24883 var has_other_nodes = false;
24884 for (var i=0;i<ar.length;i++) {
24885 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24888 // fullly contained node.
24890 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24895 // probably selected..
24896 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24897 other_nodes.push(ar[i]);
24901 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24906 has_other_nodes = true;
24908 if (!nodes.length && other_nodes.length) {
24909 nodes= other_nodes;
24911 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24917 createRange: function(sel)
24919 // this has strange effects when using with
24920 // top toolbar - not sure if it's a great idea.
24921 //this.editor.contentWindow.focus();
24922 if (typeof sel != "undefined") {
24924 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24926 return this.doc.createRange();
24929 return this.doc.createRange();
24932 getParentElement: function()
24935 this.assignDocWin();
24936 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24938 var range = this.createRange(sel);
24941 var p = range.commonAncestorContainer;
24942 while (p.nodeType == 3) { // text node
24953 * Range intersection.. the hard stuff...
24957 * [ -- selected range --- ]
24961 * if end is before start or hits it. fail.
24962 * if start is after end or hits it fail.
24964 * if either hits (but other is outside. - then it's not
24970 // @see http://www.thismuchiknow.co.uk/?p=64.
24971 rangeIntersectsNode : function(range, node)
24973 var nodeRange = node.ownerDocument.createRange();
24975 nodeRange.selectNode(node);
24977 nodeRange.selectNodeContents(node);
24980 var rangeStartRange = range.cloneRange();
24981 rangeStartRange.collapse(true);
24983 var rangeEndRange = range.cloneRange();
24984 rangeEndRange.collapse(false);
24986 var nodeStartRange = nodeRange.cloneRange();
24987 nodeStartRange.collapse(true);
24989 var nodeEndRange = nodeRange.cloneRange();
24990 nodeEndRange.collapse(false);
24992 return rangeStartRange.compareBoundaryPoints(
24993 Range.START_TO_START, nodeEndRange) == -1 &&
24994 rangeEndRange.compareBoundaryPoints(
24995 Range.START_TO_START, nodeStartRange) == 1;
24999 rangeCompareNode : function(range, node)
25001 var nodeRange = node.ownerDocument.createRange();
25003 nodeRange.selectNode(node);
25005 nodeRange.selectNodeContents(node);
25009 range.collapse(true);
25011 nodeRange.collapse(true);
25013 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25014 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25016 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25018 var nodeIsBefore = ss == 1;
25019 var nodeIsAfter = ee == -1;
25021 if (nodeIsBefore && nodeIsAfter) {
25024 if (!nodeIsBefore && nodeIsAfter) {
25025 return 1; //right trailed.
25028 if (nodeIsBefore && !nodeIsAfter) {
25029 return 2; // left trailed.
25035 // private? - in a new class?
25036 cleanUpPaste : function()
25038 // cleans up the whole document..
25039 Roo.log('cleanuppaste');
25041 this.cleanUpChildren(this.doc.body);
25042 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25043 if (clean != this.doc.body.innerHTML) {
25044 this.doc.body.innerHTML = clean;
25049 cleanWordChars : function(input) {// change the chars to hex code
25050 var he = Roo.HtmlEditorCore;
25052 var output = input;
25053 Roo.each(he.swapCodes, function(sw) {
25054 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25056 output = output.replace(swapper, sw[1]);
25063 cleanUpChildren : function (n)
25065 if (!n.childNodes.length) {
25068 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25069 this.cleanUpChild(n.childNodes[i]);
25076 cleanUpChild : function (node)
25079 //console.log(node);
25080 if (node.nodeName == "#text") {
25081 // clean up silly Windows -- stuff?
25084 if (node.nodeName == "#comment") {
25085 node.parentNode.removeChild(node);
25086 // clean up silly Windows -- stuff?
25089 var lcname = node.tagName.toLowerCase();
25090 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25091 // whitelist of tags..
25093 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25095 node.parentNode.removeChild(node);
25100 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25102 // spans with no attributes - just remove them..
25103 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25104 remove_keep_children = true;
25107 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25108 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25110 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25111 // remove_keep_children = true;
25114 if (remove_keep_children) {
25115 this.cleanUpChildren(node);
25116 // inserts everything just before this node...
25117 while (node.childNodes.length) {
25118 var cn = node.childNodes[0];
25119 node.removeChild(cn);
25120 node.parentNode.insertBefore(cn, node);
25122 node.parentNode.removeChild(node);
25126 if (!node.attributes || !node.attributes.length) {
25131 this.cleanUpChildren(node);
25135 function cleanAttr(n,v)
25138 if (v.match(/^\./) || v.match(/^\//)) {
25141 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25144 if (v.match(/^#/)) {
25147 if (v.match(/^\{/)) { // allow template editing.
25150 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25151 node.removeAttribute(n);
25155 var cwhite = this.cwhite;
25156 var cblack = this.cblack;
25158 function cleanStyle(n,v)
25160 if (v.match(/expression/)) { //XSS?? should we even bother..
25161 node.removeAttribute(n);
25165 var parts = v.split(/;/);
25168 Roo.each(parts, function(p) {
25169 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25173 var l = p.split(':').shift().replace(/\s+/g,'');
25174 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25176 if ( cwhite.length && cblack.indexOf(l) > -1) {
25177 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25178 //node.removeAttribute(n);
25182 // only allow 'c whitelisted system attributes'
25183 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25184 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25185 //node.removeAttribute(n);
25195 if (clean.length) {
25196 node.setAttribute(n, clean.join(';'));
25198 node.removeAttribute(n);
25204 for (var i = node.attributes.length-1; i > -1 ; i--) {
25205 var a = node.attributes[i];
25208 if (a.name.toLowerCase().substr(0,2)=='on') {
25209 node.removeAttribute(a.name);
25212 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25213 node.removeAttribute(a.name);
25216 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25217 cleanAttr(a.name,a.value); // fixme..
25220 if (a.name == 'style') {
25221 cleanStyle(a.name,a.value);
25224 /// clean up MS crap..
25225 // tecnically this should be a list of valid class'es..
25228 if (a.name == 'class') {
25229 if (a.value.match(/^Mso/)) {
25230 node.removeAttribute('class');
25233 if (a.value.match(/^body$/)) {
25234 node.removeAttribute('class');
25245 this.cleanUpChildren(node);
25251 * Clean up MS wordisms...
25253 cleanWord : function(node)
25256 this.cleanWord(this.doc.body);
25261 node.nodeName == 'SPAN' &&
25262 !node.hasAttributes() &&
25263 node.childNodes.length == 1 &&
25264 node.firstChild.nodeName == "#text"
25266 var textNode = node.firstChild;
25267 node.removeChild(textNode);
25268 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25269 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25271 node.parentNode.insertBefore(textNode, node);
25272 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25273 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25275 node.parentNode.removeChild(node);
25278 if (node.nodeName == "#text") {
25279 // clean up silly Windows -- stuff?
25282 if (node.nodeName == "#comment") {
25283 node.parentNode.removeChild(node);
25284 // clean up silly Windows -- stuff?
25288 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25289 node.parentNode.removeChild(node);
25292 //Roo.log(node.tagName);
25293 // remove - but keep children..
25294 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25295 //Roo.log('-- removed');
25296 while (node.childNodes.length) {
25297 var cn = node.childNodes[0];
25298 node.removeChild(cn);
25299 node.parentNode.insertBefore(cn, node);
25300 // move node to parent - and clean it..
25301 this.cleanWord(cn);
25303 node.parentNode.removeChild(node);
25304 /// no need to iterate chidlren = it's got none..
25305 //this.iterateChildren(node, this.cleanWord);
25309 if (node.className.length) {
25311 var cn = node.className.split(/\W+/);
25313 Roo.each(cn, function(cls) {
25314 if (cls.match(/Mso[a-zA-Z]+/)) {
25319 node.className = cna.length ? cna.join(' ') : '';
25321 node.removeAttribute("class");
25325 if (node.hasAttribute("lang")) {
25326 node.removeAttribute("lang");
25329 if (node.hasAttribute("style")) {
25331 var styles = node.getAttribute("style").split(";");
25333 Roo.each(styles, function(s) {
25334 if (!s.match(/:/)) {
25337 var kv = s.split(":");
25338 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25341 // what ever is left... we allow.
25344 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25345 if (!nstyle.length) {
25346 node.removeAttribute('style');
25349 this.iterateChildren(node, this.cleanWord);
25355 * iterateChildren of a Node, calling fn each time, using this as the scole..
25356 * @param {DomNode} node node to iterate children of.
25357 * @param {Function} fn method of this class to call on each item.
25359 iterateChildren : function(node, fn)
25361 if (!node.childNodes.length) {
25364 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25365 fn.call(this, node.childNodes[i])
25371 * cleanTableWidths.
25373 * Quite often pasting from word etc.. results in tables with column and widths.
25374 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25377 cleanTableWidths : function(node)
25382 this.cleanTableWidths(this.doc.body);
25387 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25390 Roo.log(node.tagName);
25391 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25392 this.iterateChildren(node, this.cleanTableWidths);
25395 if (node.hasAttribute('width')) {
25396 node.removeAttribute('width');
25400 if (node.hasAttribute("style")) {
25403 var styles = node.getAttribute("style").split(";");
25405 Roo.each(styles, function(s) {
25406 if (!s.match(/:/)) {
25409 var kv = s.split(":");
25410 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25413 // what ever is left... we allow.
25416 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25417 if (!nstyle.length) {
25418 node.removeAttribute('style');
25422 this.iterateChildren(node, this.cleanTableWidths);
25430 domToHTML : function(currentElement, depth, nopadtext) {
25432 depth = depth || 0;
25433 nopadtext = nopadtext || false;
25435 if (!currentElement) {
25436 return this.domToHTML(this.doc.body);
25439 //Roo.log(currentElement);
25441 var allText = false;
25442 var nodeName = currentElement.nodeName;
25443 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25445 if (nodeName == '#text') {
25447 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25452 if (nodeName != 'BODY') {
25455 // Prints the node tagName, such as <A>, <IMG>, etc
25458 for(i = 0; i < currentElement.attributes.length;i++) {
25460 var aname = currentElement.attributes.item(i).name;
25461 if (!currentElement.attributes.item(i).value.length) {
25464 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25467 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25476 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25479 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25484 // Traverse the tree
25486 var currentElementChild = currentElement.childNodes.item(i);
25487 var allText = true;
25488 var innerHTML = '';
25490 while (currentElementChild) {
25491 // Formatting code (indent the tree so it looks nice on the screen)
25492 var nopad = nopadtext;
25493 if (lastnode == 'SPAN') {
25497 if (currentElementChild.nodeName == '#text') {
25498 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25499 toadd = nopadtext ? toadd : toadd.trim();
25500 if (!nopad && toadd.length > 80) {
25501 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25503 innerHTML += toadd;
25506 currentElementChild = currentElement.childNodes.item(i);
25512 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25514 // Recursively traverse the tree structure of the child node
25515 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25516 lastnode = currentElementChild.nodeName;
25518 currentElementChild=currentElement.childNodes.item(i);
25524 // The remaining code is mostly for formatting the tree
25525 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25530 ret+= "</"+tagName+">";
25536 applyBlacklists : function()
25538 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25539 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25543 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25544 if (b.indexOf(tag) > -1) {
25547 this.white.push(tag);
25551 Roo.each(w, function(tag) {
25552 if (b.indexOf(tag) > -1) {
25555 if (this.white.indexOf(tag) > -1) {
25558 this.white.push(tag);
25563 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25564 if (w.indexOf(tag) > -1) {
25567 this.black.push(tag);
25571 Roo.each(b, function(tag) {
25572 if (w.indexOf(tag) > -1) {
25575 if (this.black.indexOf(tag) > -1) {
25578 this.black.push(tag);
25583 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25584 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25588 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25589 if (b.indexOf(tag) > -1) {
25592 this.cwhite.push(tag);
25596 Roo.each(w, function(tag) {
25597 if (b.indexOf(tag) > -1) {
25600 if (this.cwhite.indexOf(tag) > -1) {
25603 this.cwhite.push(tag);
25608 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25609 if (w.indexOf(tag) > -1) {
25612 this.cblack.push(tag);
25616 Roo.each(b, function(tag) {
25617 if (w.indexOf(tag) > -1) {
25620 if (this.cblack.indexOf(tag) > -1) {
25623 this.cblack.push(tag);
25628 setStylesheets : function(stylesheets)
25630 if(typeof(stylesheets) == 'string'){
25631 Roo.get(this.iframe.contentDocument.head).createChild({
25633 rel : 'stylesheet',
25642 Roo.each(stylesheets, function(s) {
25647 Roo.get(_this.iframe.contentDocument.head).createChild({
25649 rel : 'stylesheet',
25658 removeStylesheets : function()
25662 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25667 setStyle : function(style)
25669 Roo.get(this.iframe.contentDocument.head).createChild({
25678 // hide stuff that is not compatible
25692 * @event specialkey
25696 * @cfg {String} fieldClass @hide
25699 * @cfg {String} focusClass @hide
25702 * @cfg {String} autoCreate @hide
25705 * @cfg {String} inputType @hide
25708 * @cfg {String} invalidClass @hide
25711 * @cfg {String} invalidText @hide
25714 * @cfg {String} msgFx @hide
25717 * @cfg {String} validateOnBlur @hide
25721 Roo.HtmlEditorCore.white = [
25722 'area', 'br', 'img', 'input', 'hr', 'wbr',
25724 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25725 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25726 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25727 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25728 'table', 'ul', 'xmp',
25730 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25733 'dir', 'menu', 'ol', 'ul', 'dl',
25739 Roo.HtmlEditorCore.black = [
25740 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25742 'base', 'basefont', 'bgsound', 'blink', 'body',
25743 'frame', 'frameset', 'head', 'html', 'ilayer',
25744 'iframe', 'layer', 'link', 'meta', 'object',
25745 'script', 'style' ,'title', 'xml' // clean later..
25747 Roo.HtmlEditorCore.clean = [
25748 'script', 'style', 'title', 'xml'
25750 Roo.HtmlEditorCore.remove = [
25755 Roo.HtmlEditorCore.ablack = [
25759 Roo.HtmlEditorCore.aclean = [
25760 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25764 Roo.HtmlEditorCore.pwhite= [
25765 'http', 'https', 'mailto'
25768 // white listed style attributes.
25769 Roo.HtmlEditorCore.cwhite= [
25770 // 'text-align', /// default is to allow most things..
25776 // black listed style attributes.
25777 Roo.HtmlEditorCore.cblack= [
25778 // 'font-size' -- this can be set by the project
25782 Roo.HtmlEditorCore.swapCodes =[
25801 * @class Roo.bootstrap.HtmlEditor
25802 * @extends Roo.bootstrap.TextArea
25803 * Bootstrap HtmlEditor class
25806 * Create a new HtmlEditor
25807 * @param {Object} config The config object
25810 Roo.bootstrap.HtmlEditor = function(config){
25811 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25812 if (!this.toolbars) {
25813 this.toolbars = [];
25816 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25819 * @event initialize
25820 * Fires when the editor is fully initialized (including the iframe)
25821 * @param {HtmlEditor} this
25826 * Fires when the editor is first receives the focus. Any insertion must wait
25827 * until after this event.
25828 * @param {HtmlEditor} this
25832 * @event beforesync
25833 * Fires before the textarea is updated with content from the editor iframe. Return false
25834 * to cancel the sync.
25835 * @param {HtmlEditor} this
25836 * @param {String} html
25840 * @event beforepush
25841 * Fires before the iframe editor is updated with content from the textarea. Return false
25842 * to cancel the push.
25843 * @param {HtmlEditor} this
25844 * @param {String} html
25849 * Fires when the textarea is updated with content from the editor iframe.
25850 * @param {HtmlEditor} this
25851 * @param {String} html
25856 * Fires when the iframe editor is updated with content from the textarea.
25857 * @param {HtmlEditor} this
25858 * @param {String} html
25862 * @event editmodechange
25863 * Fires when the editor switches edit modes
25864 * @param {HtmlEditor} this
25865 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25867 editmodechange: true,
25869 * @event editorevent
25870 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25871 * @param {HtmlEditor} this
25875 * @event firstfocus
25876 * Fires when on first focus - needed by toolbars..
25877 * @param {HtmlEditor} this
25882 * Auto save the htmlEditor value as a file into Events
25883 * @param {HtmlEditor} this
25887 * @event savedpreview
25888 * preview the saved version of htmlEditor
25889 * @param {HtmlEditor} this
25896 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25900 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25905 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25910 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25915 * @cfg {Number} height (in pixels)
25919 * @cfg {Number} width (in pixels)
25924 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25927 stylesheets: false,
25932 // private properties
25933 validationEvent : false,
25935 initialized : false,
25938 onFocus : Roo.emptyFn,
25940 hideMode:'offsets',
25942 tbContainer : false,
25946 toolbarContainer :function() {
25947 return this.wrap.select('.x-html-editor-tb',true).first();
25951 * Protected method that will not generally be called directly. It
25952 * is called when the editor creates its toolbar. Override this method if you need to
25953 * add custom toolbar buttons.
25954 * @param {HtmlEditor} editor
25956 createToolbar : function(){
25957 Roo.log('renewing');
25958 Roo.log("create toolbars");
25960 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25961 this.toolbars[0].render(this.toolbarContainer());
25965 // if (!editor.toolbars || !editor.toolbars.length) {
25966 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25969 // for (var i =0 ; i < editor.toolbars.length;i++) {
25970 // editor.toolbars[i] = Roo.factory(
25971 // typeof(editor.toolbars[i]) == 'string' ?
25972 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25973 // Roo.bootstrap.HtmlEditor);
25974 // editor.toolbars[i].init(editor);
25980 onRender : function(ct, position)
25982 // Roo.log("Call onRender: " + this.xtype);
25984 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25986 this.wrap = this.inputEl().wrap({
25987 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25990 this.editorcore.onRender(ct, position);
25992 if (this.resizable) {
25993 this.resizeEl = new Roo.Resizable(this.wrap, {
25997 minHeight : this.height,
25998 height: this.height,
25999 handles : this.resizable,
26002 resize : function(r, w, h) {
26003 _t.onResize(w,h); // -something
26009 this.createToolbar(this);
26012 if(!this.width && this.resizable){
26013 this.setSize(this.wrap.getSize());
26015 if (this.resizeEl) {
26016 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26017 // should trigger onReize..
26023 onResize : function(w, h)
26025 Roo.log('resize: ' +w + ',' + h );
26026 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26030 if(this.inputEl() ){
26031 if(typeof w == 'number'){
26032 var aw = w - this.wrap.getFrameWidth('lr');
26033 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26036 if(typeof h == 'number'){
26037 var tbh = -11; // fixme it needs to tool bar size!
26038 for (var i =0; i < this.toolbars.length;i++) {
26039 // fixme - ask toolbars for heights?
26040 tbh += this.toolbars[i].el.getHeight();
26041 //if (this.toolbars[i].footer) {
26042 // tbh += this.toolbars[i].footer.el.getHeight();
26050 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26051 ah -= 5; // knock a few pixes off for look..
26052 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26056 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26057 this.editorcore.onResize(ew,eh);
26062 * Toggles the editor between standard and source edit mode.
26063 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26065 toggleSourceEdit : function(sourceEditMode)
26067 this.editorcore.toggleSourceEdit(sourceEditMode);
26069 if(this.editorcore.sourceEditMode){
26070 Roo.log('editor - showing textarea');
26073 // Roo.log(this.syncValue());
26075 this.inputEl().removeClass(['hide', 'x-hidden']);
26076 this.inputEl().dom.removeAttribute('tabIndex');
26077 this.inputEl().focus();
26079 Roo.log('editor - hiding textarea');
26081 // Roo.log(this.pushValue());
26084 this.inputEl().addClass(['hide', 'x-hidden']);
26085 this.inputEl().dom.setAttribute('tabIndex', -1);
26086 //this.deferFocus();
26089 if(this.resizable){
26090 this.setSize(this.wrap.getSize());
26093 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26096 // private (for BoxComponent)
26097 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26099 // private (for BoxComponent)
26100 getResizeEl : function(){
26104 // private (for BoxComponent)
26105 getPositionEl : function(){
26110 initEvents : function(){
26111 this.originalValue = this.getValue();
26115 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26118 // markInvalid : Roo.emptyFn,
26120 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26123 // clearInvalid : Roo.emptyFn,
26125 setValue : function(v){
26126 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26127 this.editorcore.pushValue();
26132 deferFocus : function(){
26133 this.focus.defer(10, this);
26137 focus : function(){
26138 this.editorcore.focus();
26144 onDestroy : function(){
26150 for (var i =0; i < this.toolbars.length;i++) {
26151 // fixme - ask toolbars for heights?
26152 this.toolbars[i].onDestroy();
26155 this.wrap.dom.innerHTML = '';
26156 this.wrap.remove();
26161 onFirstFocus : function(){
26162 //Roo.log("onFirstFocus");
26163 this.editorcore.onFirstFocus();
26164 for (var i =0; i < this.toolbars.length;i++) {
26165 this.toolbars[i].onFirstFocus();
26171 syncValue : function()
26173 this.editorcore.syncValue();
26176 pushValue : function()
26178 this.editorcore.pushValue();
26182 // hide stuff that is not compatible
26196 * @event specialkey
26200 * @cfg {String} fieldClass @hide
26203 * @cfg {String} focusClass @hide
26206 * @cfg {String} autoCreate @hide
26209 * @cfg {String} inputType @hide
26213 * @cfg {String} invalidText @hide
26216 * @cfg {String} msgFx @hide
26219 * @cfg {String} validateOnBlur @hide
26228 Roo.namespace('Roo.bootstrap.htmleditor');
26230 * @class Roo.bootstrap.HtmlEditorToolbar1
26236 new Roo.bootstrap.HtmlEditor({
26239 new Roo.bootstrap.HtmlEditorToolbar1({
26240 disable : { fonts: 1 , format: 1, ..., ... , ...],
26246 * @cfg {Object} disable List of elements to disable..
26247 * @cfg {Array} btns List of additional buttons.
26251 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26254 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26257 Roo.apply(this, config);
26259 // default disabled, based on 'good practice'..
26260 this.disable = this.disable || {};
26261 Roo.applyIf(this.disable, {
26264 specialElements : true
26266 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26268 this.editor = config.editor;
26269 this.editorcore = config.editor.editorcore;
26271 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26273 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26274 // dont call parent... till later.
26276 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26281 editorcore : false,
26286 "h1","h2","h3","h4","h5","h6",
26288 "abbr", "acronym", "address", "cite", "samp", "var",
26292 onRender : function(ct, position)
26294 // Roo.log("Call onRender: " + this.xtype);
26296 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26298 this.el.dom.style.marginBottom = '0';
26300 var editorcore = this.editorcore;
26301 var editor= this.editor;
26304 var btn = function(id,cmd , toggle, handler, html){
26306 var event = toggle ? 'toggle' : 'click';
26311 xns: Roo.bootstrap,
26315 enableToggle:toggle !== false,
26317 pressed : toggle ? false : null,
26320 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26321 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26327 // var cb_box = function...
26332 xns: Roo.bootstrap,
26337 xns: Roo.bootstrap,
26341 Roo.each(this.formats, function(f) {
26342 style.menu.items.push({
26344 xns: Roo.bootstrap,
26345 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26350 editorcore.insertTag(this.tagname);
26357 children.push(style);
26359 btn('bold',false,true);
26360 btn('italic',false,true);
26361 btn('align-left', 'justifyleft',true);
26362 btn('align-center', 'justifycenter',true);
26363 btn('align-right' , 'justifyright',true);
26364 btn('link', false, false, function(btn) {
26365 //Roo.log("create link?");
26366 var url = prompt(this.createLinkText, this.defaultLinkValue);
26367 if(url && url != 'http:/'+'/'){
26368 this.editorcore.relayCmd('createlink', url);
26371 btn('list','insertunorderedlist',true);
26372 btn('pencil', false,true, function(btn){
26374 this.toggleSourceEdit(btn.pressed);
26377 if (this.editor.btns.length > 0) {
26378 for (var i = 0; i<this.editor.btns.length; i++) {
26379 children.push(this.editor.btns[i]);
26387 xns: Roo.bootstrap,
26392 xns: Roo.bootstrap,
26397 cog.menu.items.push({
26399 xns: Roo.bootstrap,
26400 html : Clean styles,
26405 editorcore.insertTag(this.tagname);
26414 this.xtype = 'NavSimplebar';
26416 for(var i=0;i< children.length;i++) {
26418 this.buttons.add(this.addxtypeChild(children[i]));
26422 editor.on('editorevent', this.updateToolbar, this);
26424 onBtnClick : function(id)
26426 this.editorcore.relayCmd(id);
26427 this.editorcore.focus();
26431 * Protected method that will not generally be called directly. It triggers
26432 * a toolbar update by reading the markup state of the current selection in the editor.
26434 updateToolbar: function(){
26436 if(!this.editorcore.activated){
26437 this.editor.onFirstFocus(); // is this neeed?
26441 var btns = this.buttons;
26442 var doc = this.editorcore.doc;
26443 btns.get('bold').setActive(doc.queryCommandState('bold'));
26444 btns.get('italic').setActive(doc.queryCommandState('italic'));
26445 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26447 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26448 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26449 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26451 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26452 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26455 var ans = this.editorcore.getAllAncestors();
26456 if (this.formatCombo) {
26459 var store = this.formatCombo.store;
26460 this.formatCombo.setValue("");
26461 for (var i =0; i < ans.length;i++) {
26462 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26464 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26472 // hides menus... - so this cant be on a menu...
26473 Roo.bootstrap.MenuMgr.hideAll();
26475 Roo.bootstrap.MenuMgr.hideAll();
26476 //this.editorsyncValue();
26478 onFirstFocus: function() {
26479 this.buttons.each(function(item){
26483 toggleSourceEdit : function(sourceEditMode){
26486 if(sourceEditMode){
26487 Roo.log("disabling buttons");
26488 this.buttons.each( function(item){
26489 if(item.cmd != 'pencil'){
26495 Roo.log("enabling buttons");
26496 if(this.editorcore.initialized){
26497 this.buttons.each( function(item){
26503 Roo.log("calling toggole on editor");
26504 // tell the editor that it's been pressed..
26505 this.editor.toggleSourceEdit(sourceEditMode);
26519 * @class Roo.bootstrap.Markdown
26520 * @extends Roo.bootstrap.TextArea
26521 * Bootstrap Showdown editable area
26522 * @cfg {string} content
26525 * Create a new Showdown
26528 Roo.bootstrap.Markdown = function(config){
26529 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26533 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26537 initEvents : function()
26540 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26541 this.markdownEl = this.el.createChild({
26542 cls : 'roo-markdown-area'
26544 this.inputEl().addClass('d-none');
26545 if (this.getValue() == '') {
26546 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26549 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26551 this.markdownEl.on('click', this.toggleTextEdit, this);
26552 this.on('blur', this.toggleTextEdit, this);
26553 this.on('specialkey', this.resizeTextArea, this);
26556 toggleTextEdit : function()
26558 var sh = this.markdownEl.getHeight();
26559 this.inputEl().addClass('d-none');
26560 this.markdownEl.addClass('d-none');
26561 if (!this.editing) {
26563 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26564 this.inputEl().removeClass('d-none');
26565 this.inputEl().focus();
26566 this.editing = true;
26569 // show showdown...
26570 this.updateMarkdown();
26571 this.markdownEl.removeClass('d-none');
26572 this.editing = false;
26575 updateMarkdown : function()
26577 if (this.getValue() == '') {
26578 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26582 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26585 resizeTextArea: function () {
26588 Roo.log([sh, this.getValue().split("\n").length * 30]);
26589 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26591 setValue : function(val)
26593 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26594 if (!this.editing) {
26595 this.updateMarkdown();
26601 if (!this.editing) {
26602 this.toggleTextEdit();
26610 * @class Roo.bootstrap.Table.AbstractSelectionModel
26611 * @extends Roo.util.Observable
26612 * Abstract base class for grid SelectionModels. It provides the interface that should be
26613 * implemented by descendant classes. This class should not be directly instantiated.
26616 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26617 this.locked = false;
26618 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26622 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26623 /** @ignore Called by the grid automatically. Do not call directly. */
26624 init : function(grid){
26630 * Locks the selections.
26633 this.locked = true;
26637 * Unlocks the selections.
26639 unlock : function(){
26640 this.locked = false;
26644 * Returns true if the selections are locked.
26645 * @return {Boolean}
26647 isLocked : function(){
26648 return this.locked;
26652 initEvents : function ()
26658 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26659 * @class Roo.bootstrap.Table.RowSelectionModel
26660 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26661 * It supports multiple selections and keyboard selection/navigation.
26663 * @param {Object} config
26666 Roo.bootstrap.Table.RowSelectionModel = function(config){
26667 Roo.apply(this, config);
26668 this.selections = new Roo.util.MixedCollection(false, function(o){
26673 this.lastActive = false;
26677 * @event selectionchange
26678 * Fires when the selection changes
26679 * @param {SelectionModel} this
26681 "selectionchange" : true,
26683 * @event afterselectionchange
26684 * Fires after the selection changes (eg. by key press or clicking)
26685 * @param {SelectionModel} this
26687 "afterselectionchange" : true,
26689 * @event beforerowselect
26690 * Fires when a row is selected being selected, return false to cancel.
26691 * @param {SelectionModel} this
26692 * @param {Number} rowIndex The selected index
26693 * @param {Boolean} keepExisting False if other selections will be cleared
26695 "beforerowselect" : true,
26698 * Fires when a row is selected.
26699 * @param {SelectionModel} this
26700 * @param {Number} rowIndex The selected index
26701 * @param {Roo.data.Record} r The record
26703 "rowselect" : true,
26705 * @event rowdeselect
26706 * Fires when a row is deselected.
26707 * @param {SelectionModel} this
26708 * @param {Number} rowIndex The selected index
26710 "rowdeselect" : true
26712 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26713 this.locked = false;
26716 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26718 * @cfg {Boolean} singleSelect
26719 * True to allow selection of only one row at a time (defaults to false)
26721 singleSelect : false,
26724 initEvents : function()
26727 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26728 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26729 //}else{ // allow click to work like normal
26730 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26732 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26733 this.grid.on("rowclick", this.handleMouseDown, this);
26735 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26736 "up" : function(e){
26738 this.selectPrevious(e.shiftKey);
26739 }else if(this.last !== false && this.lastActive !== false){
26740 var last = this.last;
26741 this.selectRange(this.last, this.lastActive-1);
26742 this.grid.getView().focusRow(this.lastActive);
26743 if(last !== false){
26747 this.selectFirstRow();
26749 this.fireEvent("afterselectionchange", this);
26751 "down" : function(e){
26753 this.selectNext(e.shiftKey);
26754 }else if(this.last !== false && this.lastActive !== false){
26755 var last = this.last;
26756 this.selectRange(this.last, this.lastActive+1);
26757 this.grid.getView().focusRow(this.lastActive);
26758 if(last !== false){
26762 this.selectFirstRow();
26764 this.fireEvent("afterselectionchange", this);
26768 this.grid.store.on('load', function(){
26769 this.selections.clear();
26772 var view = this.grid.view;
26773 view.on("refresh", this.onRefresh, this);
26774 view.on("rowupdated", this.onRowUpdated, this);
26775 view.on("rowremoved", this.onRemove, this);
26780 onRefresh : function()
26782 var ds = this.grid.store, i, v = this.grid.view;
26783 var s = this.selections;
26784 s.each(function(r){
26785 if((i = ds.indexOfId(r.id)) != -1){
26794 onRemove : function(v, index, r){
26795 this.selections.remove(r);
26799 onRowUpdated : function(v, index, r){
26800 if(this.isSelected(r)){
26801 v.onRowSelect(index);
26807 * @param {Array} records The records to select
26808 * @param {Boolean} keepExisting (optional) True to keep existing selections
26810 selectRecords : function(records, keepExisting)
26813 this.clearSelections();
26815 var ds = this.grid.store;
26816 for(var i = 0, len = records.length; i < len; i++){
26817 this.selectRow(ds.indexOf(records[i]), true);
26822 * Gets the number of selected rows.
26825 getCount : function(){
26826 return this.selections.length;
26830 * Selects the first row in the grid.
26832 selectFirstRow : function(){
26837 * Select the last row.
26838 * @param {Boolean} keepExisting (optional) True to keep existing selections
26840 selectLastRow : function(keepExisting){
26841 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26842 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26846 * Selects the row immediately following the last selected row.
26847 * @param {Boolean} keepExisting (optional) True to keep existing selections
26849 selectNext : function(keepExisting)
26851 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26852 this.selectRow(this.last+1, keepExisting);
26853 this.grid.getView().focusRow(this.last);
26858 * Selects the row that precedes the last selected row.
26859 * @param {Boolean} keepExisting (optional) True to keep existing selections
26861 selectPrevious : function(keepExisting){
26863 this.selectRow(this.last-1, keepExisting);
26864 this.grid.getView().focusRow(this.last);
26869 * Returns the selected records
26870 * @return {Array} Array of selected records
26872 getSelections : function(){
26873 return [].concat(this.selections.items);
26877 * Returns the first selected record.
26880 getSelected : function(){
26881 return this.selections.itemAt(0);
26886 * Clears all selections.
26888 clearSelections : function(fast)
26894 var ds = this.grid.store;
26895 var s = this.selections;
26896 s.each(function(r){
26897 this.deselectRow(ds.indexOfId(r.id));
26901 this.selections.clear();
26908 * Selects all rows.
26910 selectAll : function(){
26914 this.selections.clear();
26915 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26916 this.selectRow(i, true);
26921 * Returns True if there is a selection.
26922 * @return {Boolean}
26924 hasSelection : function(){
26925 return this.selections.length > 0;
26929 * Returns True if the specified row is selected.
26930 * @param {Number/Record} record The record or index of the record to check
26931 * @return {Boolean}
26933 isSelected : function(index){
26934 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26935 return (r && this.selections.key(r.id) ? true : false);
26939 * Returns True if the specified record id is selected.
26940 * @param {String} id The id of record to check
26941 * @return {Boolean}
26943 isIdSelected : function(id){
26944 return (this.selections.key(id) ? true : false);
26949 handleMouseDBClick : function(e, t){
26953 handleMouseDown : function(e, t)
26955 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26956 if(this.isLocked() || rowIndex < 0 ){
26959 if(e.shiftKey && this.last !== false){
26960 var last = this.last;
26961 this.selectRange(last, rowIndex, e.ctrlKey);
26962 this.last = last; // reset the last
26966 var isSelected = this.isSelected(rowIndex);
26967 //Roo.log("select row:" + rowIndex);
26969 this.deselectRow(rowIndex);
26971 this.selectRow(rowIndex, true);
26975 if(e.button !== 0 && isSelected){
26976 alert('rowIndex 2: ' + rowIndex);
26977 view.focusRow(rowIndex);
26978 }else if(e.ctrlKey && isSelected){
26979 this.deselectRow(rowIndex);
26980 }else if(!isSelected){
26981 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26982 view.focusRow(rowIndex);
26986 this.fireEvent("afterselectionchange", this);
26989 handleDragableRowClick : function(grid, rowIndex, e)
26991 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26992 this.selectRow(rowIndex, false);
26993 grid.view.focusRow(rowIndex);
26994 this.fireEvent("afterselectionchange", this);
26999 * Selects multiple rows.
27000 * @param {Array} rows Array of the indexes of the row to select
27001 * @param {Boolean} keepExisting (optional) True to keep existing selections
27003 selectRows : function(rows, keepExisting){
27005 this.clearSelections();
27007 for(var i = 0, len = rows.length; i < len; i++){
27008 this.selectRow(rows[i], true);
27013 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27014 * @param {Number} startRow The index of the first row in the range
27015 * @param {Number} endRow The index of the last row in the range
27016 * @param {Boolean} keepExisting (optional) True to retain existing selections
27018 selectRange : function(startRow, endRow, keepExisting){
27023 this.clearSelections();
27025 if(startRow <= endRow){
27026 for(var i = startRow; i <= endRow; i++){
27027 this.selectRow(i, true);
27030 for(var i = startRow; i >= endRow; i--){
27031 this.selectRow(i, true);
27037 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27038 * @param {Number} startRow The index of the first row in the range
27039 * @param {Number} endRow The index of the last row in the range
27041 deselectRange : function(startRow, endRow, preventViewNotify){
27045 for(var i = startRow; i <= endRow; i++){
27046 this.deselectRow(i, preventViewNotify);
27052 * @param {Number} row The index of the row to select
27053 * @param {Boolean} keepExisting (optional) True to keep existing selections
27055 selectRow : function(index, keepExisting, preventViewNotify)
27057 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27060 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27061 if(!keepExisting || this.singleSelect){
27062 this.clearSelections();
27065 var r = this.grid.store.getAt(index);
27066 //console.log('selectRow - record id :' + r.id);
27068 this.selections.add(r);
27069 this.last = this.lastActive = index;
27070 if(!preventViewNotify){
27071 var proxy = new Roo.Element(
27072 this.grid.getRowDom(index)
27074 proxy.addClass('bg-info info');
27076 this.fireEvent("rowselect", this, index, r);
27077 this.fireEvent("selectionchange", this);
27083 * @param {Number} row The index of the row to deselect
27085 deselectRow : function(index, preventViewNotify)
27090 if(this.last == index){
27093 if(this.lastActive == index){
27094 this.lastActive = false;
27097 var r = this.grid.store.getAt(index);
27102 this.selections.remove(r);
27103 //.console.log('deselectRow - record id :' + r.id);
27104 if(!preventViewNotify){
27106 var proxy = new Roo.Element(
27107 this.grid.getRowDom(index)
27109 proxy.removeClass('bg-info info');
27111 this.fireEvent("rowdeselect", this, index);
27112 this.fireEvent("selectionchange", this);
27116 restoreLast : function(){
27118 this.last = this._last;
27123 acceptsNav : function(row, col, cm){
27124 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27128 onEditorKey : function(field, e){
27129 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27134 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27136 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27138 }else if(k == e.ENTER && !e.ctrlKey){
27142 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27144 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27146 }else if(k == e.ESC){
27150 g.startEditing(newCell[0], newCell[1]);
27156 * Ext JS Library 1.1.1
27157 * Copyright(c) 2006-2007, Ext JS, LLC.
27159 * Originally Released Under LGPL - original licence link has changed is not relivant.
27162 * <script type="text/javascript">
27166 * @class Roo.bootstrap.PagingToolbar
27167 * @extends Roo.bootstrap.NavSimplebar
27168 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27170 * Create a new PagingToolbar
27171 * @param {Object} config The config object
27172 * @param {Roo.data.Store} store
27174 Roo.bootstrap.PagingToolbar = function(config)
27176 // old args format still supported... - xtype is prefered..
27177 // created from xtype...
27179 this.ds = config.dataSource;
27181 if (config.store && !this.ds) {
27182 this.store= Roo.factory(config.store, Roo.data);
27183 this.ds = this.store;
27184 this.ds.xmodule = this.xmodule || false;
27187 this.toolbarItems = [];
27188 if (config.items) {
27189 this.toolbarItems = config.items;
27192 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27197 this.bind(this.ds);
27200 if (Roo.bootstrap.version == 4) {
27201 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27203 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27208 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27210 * @cfg {Roo.data.Store} dataSource
27211 * The underlying data store providing the paged data
27214 * @cfg {String/HTMLElement/Element} container
27215 * container The id or element that will contain the toolbar
27218 * @cfg {Boolean} displayInfo
27219 * True to display the displayMsg (defaults to false)
27222 * @cfg {Number} pageSize
27223 * The number of records to display per page (defaults to 20)
27227 * @cfg {String} displayMsg
27228 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27230 displayMsg : 'Displaying {0} - {1} of {2}',
27232 * @cfg {String} emptyMsg
27233 * The message to display when no records are found (defaults to "No data to display")
27235 emptyMsg : 'No data to display',
27237 * Customizable piece of the default paging text (defaults to "Page")
27240 beforePageText : "Page",
27242 * Customizable piece of the default paging text (defaults to "of %0")
27245 afterPageText : "of {0}",
27247 * Customizable piece of the default paging text (defaults to "First Page")
27250 firstText : "First Page",
27252 * Customizable piece of the default paging text (defaults to "Previous Page")
27255 prevText : "Previous Page",
27257 * Customizable piece of the default paging text (defaults to "Next Page")
27260 nextText : "Next Page",
27262 * Customizable piece of the default paging text (defaults to "Last Page")
27265 lastText : "Last Page",
27267 * Customizable piece of the default paging text (defaults to "Refresh")
27270 refreshText : "Refresh",
27274 onRender : function(ct, position)
27276 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27277 this.navgroup.parentId = this.id;
27278 this.navgroup.onRender(this.el, null);
27279 // add the buttons to the navgroup
27281 if(this.displayInfo){
27282 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27283 this.displayEl = this.el.select('.x-paging-info', true).first();
27284 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27285 // this.displayEl = navel.el.select('span',true).first();
27291 Roo.each(_this.buttons, function(e){ // this might need to use render????
27292 Roo.factory(e).render(_this.el);
27296 Roo.each(_this.toolbarItems, function(e) {
27297 _this.navgroup.addItem(e);
27301 this.first = this.navgroup.addItem({
27302 tooltip: this.firstText,
27303 cls: "prev btn-outline-secondary",
27304 html : ' <i class="fa fa-step-backward"></i>',
27306 preventDefault: true,
27307 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27310 this.prev = this.navgroup.addItem({
27311 tooltip: this.prevText,
27312 cls: "prev btn-outline-secondary",
27313 html : ' <i class="fa fa-backward"></i>',
27315 preventDefault: true,
27316 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27318 //this.addSeparator();
27321 var field = this.navgroup.addItem( {
27323 cls : 'x-paging-position btn-outline-secondary',
27325 html : this.beforePageText +
27326 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27327 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27330 this.field = field.el.select('input', true).first();
27331 this.field.on("keydown", this.onPagingKeydown, this);
27332 this.field.on("focus", function(){this.dom.select();});
27335 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27336 //this.field.setHeight(18);
27337 //this.addSeparator();
27338 this.next = this.navgroup.addItem({
27339 tooltip: this.nextText,
27340 cls: "next btn-outline-secondary",
27341 html : ' <i class="fa fa-forward"></i>',
27343 preventDefault: true,
27344 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27346 this.last = this.navgroup.addItem({
27347 tooltip: this.lastText,
27348 html : ' <i class="fa fa-step-forward"></i>',
27349 cls: "next btn-outline-secondary",
27351 preventDefault: true,
27352 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27354 //this.addSeparator();
27355 this.loading = this.navgroup.addItem({
27356 tooltip: this.refreshText,
27357 cls: "btn-outline-secondary",
27358 html : ' <i class="fa fa-refresh"></i>',
27359 preventDefault: true,
27360 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27366 updateInfo : function(){
27367 if(this.displayEl){
27368 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27369 var msg = count == 0 ?
27373 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27375 this.displayEl.update(msg);
27380 onLoad : function(ds, r, o)
27382 this.cursor = o.params && o.params.start ? o.params.start : 0;
27384 var d = this.getPageData(),
27389 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27390 this.field.dom.value = ap;
27391 this.first.setDisabled(ap == 1);
27392 this.prev.setDisabled(ap == 1);
27393 this.next.setDisabled(ap == ps);
27394 this.last.setDisabled(ap == ps);
27395 this.loading.enable();
27400 getPageData : function(){
27401 var total = this.ds.getTotalCount();
27404 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27405 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27410 onLoadError : function(){
27411 this.loading.enable();
27415 onPagingKeydown : function(e){
27416 var k = e.getKey();
27417 var d = this.getPageData();
27419 var v = this.field.dom.value, pageNum;
27420 if(!v || isNaN(pageNum = parseInt(v, 10))){
27421 this.field.dom.value = d.activePage;
27424 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27425 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27428 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))
27430 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27431 this.field.dom.value = pageNum;
27432 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27435 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27437 var v = this.field.dom.value, pageNum;
27438 var increment = (e.shiftKey) ? 10 : 1;
27439 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27442 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27443 this.field.dom.value = d.activePage;
27446 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27448 this.field.dom.value = parseInt(v, 10) + increment;
27449 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27450 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27457 beforeLoad : function(){
27459 this.loading.disable();
27464 onClick : function(which){
27473 ds.load({params:{start: 0, limit: this.pageSize}});
27476 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27479 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27482 var total = ds.getTotalCount();
27483 var extra = total % this.pageSize;
27484 var lastStart = extra ? (total - extra) : total-this.pageSize;
27485 ds.load({params:{start: lastStart, limit: this.pageSize}});
27488 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27494 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27495 * @param {Roo.data.Store} store The data store to unbind
27497 unbind : function(ds){
27498 ds.un("beforeload", this.beforeLoad, this);
27499 ds.un("load", this.onLoad, this);
27500 ds.un("loadexception", this.onLoadError, this);
27501 ds.un("remove", this.updateInfo, this);
27502 ds.un("add", this.updateInfo, this);
27503 this.ds = undefined;
27507 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27508 * @param {Roo.data.Store} store The data store to bind
27510 bind : function(ds){
27511 ds.on("beforeload", this.beforeLoad, this);
27512 ds.on("load", this.onLoad, this);
27513 ds.on("loadexception", this.onLoadError, this);
27514 ds.on("remove", this.updateInfo, this);
27515 ds.on("add", this.updateInfo, this);
27526 * @class Roo.bootstrap.MessageBar
27527 * @extends Roo.bootstrap.Component
27528 * Bootstrap MessageBar class
27529 * @cfg {String} html contents of the MessageBar
27530 * @cfg {String} weight (info | success | warning | danger) default info
27531 * @cfg {String} beforeClass insert the bar before the given class
27532 * @cfg {Boolean} closable (true | false) default false
27533 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27536 * Create a new Element
27537 * @param {Object} config The config object
27540 Roo.bootstrap.MessageBar = function(config){
27541 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27544 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27550 beforeClass: 'bootstrap-sticky-wrap',
27552 getAutoCreate : function(){
27556 cls: 'alert alert-dismissable alert-' + this.weight,
27561 html: this.html || ''
27567 cfg.cls += ' alert-messages-fixed';
27581 onRender : function(ct, position)
27583 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27586 var cfg = Roo.apply({}, this.getAutoCreate());
27590 cfg.cls += ' ' + this.cls;
27593 cfg.style = this.style;
27595 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27597 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27600 this.el.select('>button.close').on('click', this.hide, this);
27606 if (!this.rendered) {
27612 this.fireEvent('show', this);
27618 if (!this.rendered) {
27624 this.fireEvent('hide', this);
27627 update : function()
27629 // var e = this.el.dom.firstChild;
27631 // if(this.closable){
27632 // e = e.nextSibling;
27635 // e.data = this.html || '';
27637 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27653 * @class Roo.bootstrap.Graph
27654 * @extends Roo.bootstrap.Component
27655 * Bootstrap Graph class
27659 @cfg {String} graphtype bar | vbar | pie
27660 @cfg {number} g_x coodinator | centre x (pie)
27661 @cfg {number} g_y coodinator | centre y (pie)
27662 @cfg {number} g_r radius (pie)
27663 @cfg {number} g_height height of the chart (respected by all elements in the set)
27664 @cfg {number} g_width width of the chart (respected by all elements in the set)
27665 @cfg {Object} title The title of the chart
27668 -opts (object) options for the chart
27670 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27671 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27673 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.
27674 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27676 o stretch (boolean)
27678 -opts (object) options for the pie
27681 o startAngle (number)
27682 o endAngle (number)
27686 * Create a new Input
27687 * @param {Object} config The config object
27690 Roo.bootstrap.Graph = function(config){
27691 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27697 * The img click event for the img.
27698 * @param {Roo.EventObject} e
27704 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27715 //g_colors: this.colors,
27722 getAutoCreate : function(){
27733 onRender : function(ct,position){
27736 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27738 if (typeof(Raphael) == 'undefined') {
27739 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27743 this.raphael = Raphael(this.el.dom);
27745 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27746 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27747 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27748 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27750 r.text(160, 10, "Single Series Chart").attr(txtattr);
27751 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27752 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27753 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27755 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27756 r.barchart(330, 10, 300, 220, data1);
27757 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27758 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27761 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27762 // r.barchart(30, 30, 560, 250, xdata, {
27763 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27764 // axis : "0 0 1 1",
27765 // axisxlabels : xdata
27766 // //yvalues : cols,
27769 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27771 // this.load(null,xdata,{
27772 // axis : "0 0 1 1",
27773 // axisxlabels : xdata
27778 load : function(graphtype,xdata,opts)
27780 this.raphael.clear();
27782 graphtype = this.graphtype;
27787 var r = this.raphael,
27788 fin = function () {
27789 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27791 fout = function () {
27792 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27794 pfin = function() {
27795 this.sector.stop();
27796 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27799 this.label[0].stop();
27800 this.label[0].attr({ r: 7.5 });
27801 this.label[1].attr({ "font-weight": 800 });
27804 pfout = function() {
27805 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27808 this.label[0].animate({ r: 5 }, 500, "bounce");
27809 this.label[1].attr({ "font-weight": 400 });
27815 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27818 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27821 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27822 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27824 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27831 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27836 setTitle: function(o)
27841 initEvents: function() {
27844 this.el.on('click', this.onClick, this);
27848 onClick : function(e)
27850 Roo.log('img onclick');
27851 this.fireEvent('click', this, e);
27863 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27866 * @class Roo.bootstrap.dash.NumberBox
27867 * @extends Roo.bootstrap.Component
27868 * Bootstrap NumberBox class
27869 * @cfg {String} headline Box headline
27870 * @cfg {String} content Box content
27871 * @cfg {String} icon Box icon
27872 * @cfg {String} footer Footer text
27873 * @cfg {String} fhref Footer href
27876 * Create a new NumberBox
27877 * @param {Object} config The config object
27881 Roo.bootstrap.dash.NumberBox = function(config){
27882 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27886 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27895 getAutoCreate : function(){
27899 cls : 'small-box ',
27907 cls : 'roo-headline',
27908 html : this.headline
27912 cls : 'roo-content',
27913 html : this.content
27927 cls : 'ion ' + this.icon
27936 cls : 'small-box-footer',
27937 href : this.fhref || '#',
27941 cfg.cn.push(footer);
27948 onRender : function(ct,position){
27949 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27956 setHeadline: function (value)
27958 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27961 setFooter: function (value, href)
27963 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27966 this.el.select('a.small-box-footer',true).first().attr('href', href);
27971 setContent: function (value)
27973 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27976 initEvents: function()
27990 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27993 * @class Roo.bootstrap.dash.TabBox
27994 * @extends Roo.bootstrap.Component
27995 * Bootstrap TabBox class
27996 * @cfg {String} title Title of the TabBox
27997 * @cfg {String} icon Icon of the TabBox
27998 * @cfg {Boolean} showtabs (true|false) show the tabs default true
27999 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28002 * Create a new TabBox
28003 * @param {Object} config The config object
28007 Roo.bootstrap.dash.TabBox = function(config){
28008 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28013 * When a pane is added
28014 * @param {Roo.bootstrap.dash.TabPane} pane
28018 * @event activatepane
28019 * When a pane is activated
28020 * @param {Roo.bootstrap.dash.TabPane} pane
28022 "activatepane" : true
28030 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28035 tabScrollable : false,
28037 getChildContainer : function()
28039 return this.el.select('.tab-content', true).first();
28042 getAutoCreate : function(){
28046 cls: 'pull-left header',
28054 cls: 'fa ' + this.icon
28060 cls: 'nav nav-tabs pull-right',
28066 if(this.tabScrollable){
28073 cls: 'nav nav-tabs pull-right',
28084 cls: 'nav-tabs-custom',
28089 cls: 'tab-content no-padding',
28097 initEvents : function()
28099 //Roo.log('add add pane handler');
28100 this.on('addpane', this.onAddPane, this);
28103 * Updates the box title
28104 * @param {String} html to set the title to.
28106 setTitle : function(value)
28108 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28110 onAddPane : function(pane)
28112 this.panes.push(pane);
28113 //Roo.log('addpane');
28115 // tabs are rendere left to right..
28116 if(!this.showtabs){
28120 var ctr = this.el.select('.nav-tabs', true).first();
28123 var existing = ctr.select('.nav-tab',true);
28124 var qty = existing.getCount();;
28127 var tab = ctr.createChild({
28129 cls : 'nav-tab' + (qty ? '' : ' active'),
28137 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28140 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28142 pane.el.addClass('active');
28147 onTabClick : function(ev,un,ob,pane)
28149 //Roo.log('tab - prev default');
28150 ev.preventDefault();
28153 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28154 pane.tab.addClass('active');
28155 //Roo.log(pane.title);
28156 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28157 // technically we should have a deactivate event.. but maybe add later.
28158 // and it should not de-activate the selected tab...
28159 this.fireEvent('activatepane', pane);
28160 pane.el.addClass('active');
28161 pane.fireEvent('activate');
28166 getActivePane : function()
28169 Roo.each(this.panes, function(p) {
28170 if(p.el.hasClass('active')){
28191 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28193 * @class Roo.bootstrap.TabPane
28194 * @extends Roo.bootstrap.Component
28195 * Bootstrap TabPane class
28196 * @cfg {Boolean} active (false | true) Default false
28197 * @cfg {String} title title of panel
28201 * Create a new TabPane
28202 * @param {Object} config The config object
28205 Roo.bootstrap.dash.TabPane = function(config){
28206 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28212 * When a pane is activated
28213 * @param {Roo.bootstrap.dash.TabPane} pane
28220 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28225 // the tabBox that this is attached to.
28228 getAutoCreate : function()
28236 cfg.cls += ' active';
28241 initEvents : function()
28243 //Roo.log('trigger add pane handler');
28244 this.parent().fireEvent('addpane', this)
28248 * Updates the tab title
28249 * @param {String} html to set the title to.
28251 setTitle: function(str)
28257 this.tab.select('a', true).first().dom.innerHTML = str;
28274 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28277 * @class Roo.bootstrap.menu.Menu
28278 * @extends Roo.bootstrap.Component
28279 * Bootstrap Menu class - container for Menu
28280 * @cfg {String} html Text of the menu
28281 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28282 * @cfg {String} icon Font awesome icon
28283 * @cfg {String} pos Menu align to (top | bottom) default bottom
28287 * Create a new Menu
28288 * @param {Object} config The config object
28292 Roo.bootstrap.menu.Menu = function(config){
28293 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28297 * @event beforeshow
28298 * Fires before this menu is displayed
28299 * @param {Roo.bootstrap.menu.Menu} this
28303 * @event beforehide
28304 * Fires before this menu is hidden
28305 * @param {Roo.bootstrap.menu.Menu} this
28310 * Fires after this menu is displayed
28311 * @param {Roo.bootstrap.menu.Menu} this
28316 * Fires after this menu is hidden
28317 * @param {Roo.bootstrap.menu.Menu} this
28322 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28323 * @param {Roo.bootstrap.menu.Menu} this
28324 * @param {Roo.EventObject} e
28331 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28335 weight : 'default',
28340 getChildContainer : function() {
28341 if(this.isSubMenu){
28345 return this.el.select('ul.dropdown-menu', true).first();
28348 getAutoCreate : function()
28353 cls : 'roo-menu-text',
28361 cls : 'fa ' + this.icon
28372 cls : 'dropdown-button btn btn-' + this.weight,
28377 cls : 'dropdown-toggle btn btn-' + this.weight,
28387 cls : 'dropdown-menu'
28393 if(this.pos == 'top'){
28394 cfg.cls += ' dropup';
28397 if(this.isSubMenu){
28400 cls : 'dropdown-menu'
28407 onRender : function(ct, position)
28409 this.isSubMenu = ct.hasClass('dropdown-submenu');
28411 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28414 initEvents : function()
28416 if(this.isSubMenu){
28420 this.hidden = true;
28422 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28423 this.triggerEl.on('click', this.onTriggerPress, this);
28425 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28426 this.buttonEl.on('click', this.onClick, this);
28432 if(this.isSubMenu){
28436 return this.el.select('ul.dropdown-menu', true).first();
28439 onClick : function(e)
28441 this.fireEvent("click", this, e);
28444 onTriggerPress : function(e)
28446 if (this.isVisible()) {
28453 isVisible : function(){
28454 return !this.hidden;
28459 this.fireEvent("beforeshow", this);
28461 this.hidden = false;
28462 this.el.addClass('open');
28464 Roo.get(document).on("mouseup", this.onMouseUp, this);
28466 this.fireEvent("show", this);
28473 this.fireEvent("beforehide", this);
28475 this.hidden = true;
28476 this.el.removeClass('open');
28478 Roo.get(document).un("mouseup", this.onMouseUp);
28480 this.fireEvent("hide", this);
28483 onMouseUp : function()
28497 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28500 * @class Roo.bootstrap.menu.Item
28501 * @extends Roo.bootstrap.Component
28502 * Bootstrap MenuItem class
28503 * @cfg {Boolean} submenu (true | false) default false
28504 * @cfg {String} html text of the item
28505 * @cfg {String} href the link
28506 * @cfg {Boolean} disable (true | false) default false
28507 * @cfg {Boolean} preventDefault (true | false) default true
28508 * @cfg {String} icon Font awesome icon
28509 * @cfg {String} pos Submenu align to (left | right) default right
28513 * Create a new Item
28514 * @param {Object} config The config object
28518 Roo.bootstrap.menu.Item = function(config){
28519 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28523 * Fires when the mouse is hovering over this menu
28524 * @param {Roo.bootstrap.menu.Item} this
28525 * @param {Roo.EventObject} e
28530 * Fires when the mouse exits this menu
28531 * @param {Roo.bootstrap.menu.Item} this
28532 * @param {Roo.EventObject} e
28538 * The raw click event for the entire grid.
28539 * @param {Roo.EventObject} e
28545 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28550 preventDefault: true,
28555 getAutoCreate : function()
28560 cls : 'roo-menu-item-text',
28568 cls : 'fa ' + this.icon
28577 href : this.href || '#',
28584 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28588 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28590 if(this.pos == 'left'){
28591 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28598 initEvents : function()
28600 this.el.on('mouseover', this.onMouseOver, this);
28601 this.el.on('mouseout', this.onMouseOut, this);
28603 this.el.select('a', true).first().on('click', this.onClick, this);
28607 onClick : function(e)
28609 if(this.preventDefault){
28610 e.preventDefault();
28613 this.fireEvent("click", this, e);
28616 onMouseOver : function(e)
28618 if(this.submenu && this.pos == 'left'){
28619 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28622 this.fireEvent("mouseover", this, e);
28625 onMouseOut : function(e)
28627 this.fireEvent("mouseout", this, e);
28639 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28642 * @class Roo.bootstrap.menu.Separator
28643 * @extends Roo.bootstrap.Component
28644 * Bootstrap Separator class
28647 * Create a new Separator
28648 * @param {Object} config The config object
28652 Roo.bootstrap.menu.Separator = function(config){
28653 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28656 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28658 getAutoCreate : function(){
28679 * @class Roo.bootstrap.Tooltip
28680 * Bootstrap Tooltip class
28681 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28682 * to determine which dom element triggers the tooltip.
28684 * It needs to add support for additional attributes like tooltip-position
28687 * Create a new Toolti
28688 * @param {Object} config The config object
28691 Roo.bootstrap.Tooltip = function(config){
28692 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28694 this.alignment = Roo.bootstrap.Tooltip.alignment;
28696 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28697 this.alignment = config.alignment;
28702 Roo.apply(Roo.bootstrap.Tooltip, {
28704 * @function init initialize tooltip monitoring.
28708 currentTip : false,
28709 currentRegion : false,
28715 Roo.get(document).on('mouseover', this.enter ,this);
28716 Roo.get(document).on('mouseout', this.leave, this);
28719 this.currentTip = new Roo.bootstrap.Tooltip();
28722 enter : function(ev)
28724 var dom = ev.getTarget();
28726 //Roo.log(['enter',dom]);
28727 var el = Roo.fly(dom);
28728 if (this.currentEl) {
28730 //Roo.log(this.currentEl);
28731 //Roo.log(this.currentEl.contains(dom));
28732 if (this.currentEl == el) {
28735 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28741 if (this.currentTip.el) {
28742 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28746 if(!el || el.dom == document){
28752 // you can not look for children, as if el is the body.. then everythign is the child..
28753 if (!el.attr('tooltip')) { //
28754 if (!el.select("[tooltip]").elements.length) {
28757 // is the mouse over this child...?
28758 bindEl = el.select("[tooltip]").first();
28759 var xy = ev.getXY();
28760 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28761 //Roo.log("not in region.");
28764 //Roo.log("child element over..");
28767 this.currentEl = bindEl;
28768 this.currentTip.bind(bindEl);
28769 this.currentRegion = Roo.lib.Region.getRegion(dom);
28770 this.currentTip.enter();
28773 leave : function(ev)
28775 var dom = ev.getTarget();
28776 //Roo.log(['leave',dom]);
28777 if (!this.currentEl) {
28782 if (dom != this.currentEl.dom) {
28785 var xy = ev.getXY();
28786 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28789 // only activate leave if mouse cursor is outside... bounding box..
28794 if (this.currentTip) {
28795 this.currentTip.leave();
28797 //Roo.log('clear currentEl');
28798 this.currentEl = false;
28803 'left' : ['r-l', [-2,0], 'right'],
28804 'right' : ['l-r', [2,0], 'left'],
28805 'bottom' : ['t-b', [0,2], 'top'],
28806 'top' : [ 'b-t', [0,-2], 'bottom']
28812 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28817 delay : null, // can be { show : 300 , hide: 500}
28821 hoverState : null, //???
28823 placement : 'bottom',
28827 getAutoCreate : function(){
28834 cls : 'tooltip-arrow arrow'
28837 cls : 'tooltip-inner'
28844 bind : function(el)
28849 initEvents : function()
28851 this.arrowEl = this.el.select('.arrow', true).first();
28852 this.innerEl = this.el.select('.tooltip-inner', true).first();
28855 enter : function () {
28857 if (this.timeout != null) {
28858 clearTimeout(this.timeout);
28861 this.hoverState = 'in';
28862 //Roo.log("enter - show");
28863 if (!this.delay || !this.delay.show) {
28868 this.timeout = setTimeout(function () {
28869 if (_t.hoverState == 'in') {
28872 }, this.delay.show);
28876 clearTimeout(this.timeout);
28878 this.hoverState = 'out';
28879 if (!this.delay || !this.delay.hide) {
28885 this.timeout = setTimeout(function () {
28886 //Roo.log("leave - timeout");
28888 if (_t.hoverState == 'out') {
28890 Roo.bootstrap.Tooltip.currentEl = false;
28895 show : function (msg)
28898 this.render(document.body);
28901 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28903 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28905 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28907 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28908 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28910 var placement = typeof this.placement == 'function' ?
28911 this.placement.call(this, this.el, on_el) :
28914 var autoToken = /\s?auto?\s?/i;
28915 var autoPlace = autoToken.test(placement);
28917 placement = placement.replace(autoToken, '') || 'top';
28921 //this.el.setXY([0,0]);
28923 //this.el.dom.style.display='block';
28925 //this.el.appendTo(on_el);
28927 var p = this.getPosition();
28928 var box = this.el.getBox();
28934 var align = this.alignment[placement];
28936 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28938 if(placement == 'top' || placement == 'bottom'){
28940 placement = 'right';
28943 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28944 placement = 'left';
28947 var scroll = Roo.select('body', true).first().getScroll();
28949 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28953 align = this.alignment[placement];
28955 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28959 this.el.alignTo(this.bindEl, align[0],align[1]);
28960 //var arrow = this.el.select('.arrow',true).first();
28961 //arrow.set(align[2],
28963 this.el.addClass(placement);
28964 this.el.addClass("bs-tooltip-"+ placement);
28966 this.el.addClass('in fade show');
28968 this.hoverState = null;
28970 if (this.el.hasClass('fade')) {
28985 //this.el.setXY([0,0]);
28986 this.el.removeClass(['show', 'in']);
29002 * @class Roo.bootstrap.LocationPicker
29003 * @extends Roo.bootstrap.Component
29004 * Bootstrap LocationPicker class
29005 * @cfg {Number} latitude Position when init default 0
29006 * @cfg {Number} longitude Position when init default 0
29007 * @cfg {Number} zoom default 15
29008 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29009 * @cfg {Boolean} mapTypeControl default false
29010 * @cfg {Boolean} disableDoubleClickZoom default false
29011 * @cfg {Boolean} scrollwheel default true
29012 * @cfg {Boolean} streetViewControl default false
29013 * @cfg {Number} radius default 0
29014 * @cfg {String} locationName
29015 * @cfg {Boolean} draggable default true
29016 * @cfg {Boolean} enableAutocomplete default false
29017 * @cfg {Boolean} enableReverseGeocode default true
29018 * @cfg {String} markerTitle
29021 * Create a new LocationPicker
29022 * @param {Object} config The config object
29026 Roo.bootstrap.LocationPicker = function(config){
29028 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29033 * Fires when the picker initialized.
29034 * @param {Roo.bootstrap.LocationPicker} this
29035 * @param {Google Location} location
29039 * @event positionchanged
29040 * Fires when the picker position changed.
29041 * @param {Roo.bootstrap.LocationPicker} this
29042 * @param {Google Location} location
29044 positionchanged : true,
29047 * Fires when the map resize.
29048 * @param {Roo.bootstrap.LocationPicker} this
29053 * Fires when the map show.
29054 * @param {Roo.bootstrap.LocationPicker} this
29059 * Fires when the map hide.
29060 * @param {Roo.bootstrap.LocationPicker} this
29065 * Fires when click the map.
29066 * @param {Roo.bootstrap.LocationPicker} this
29067 * @param {Map event} e
29071 * @event mapRightClick
29072 * Fires when right click the map.
29073 * @param {Roo.bootstrap.LocationPicker} this
29074 * @param {Map event} e
29076 mapRightClick : true,
29078 * @event markerClick
29079 * Fires when click the marker.
29080 * @param {Roo.bootstrap.LocationPicker} this
29081 * @param {Map event} e
29083 markerClick : true,
29085 * @event markerRightClick
29086 * Fires when right click the marker.
29087 * @param {Roo.bootstrap.LocationPicker} this
29088 * @param {Map event} e
29090 markerRightClick : true,
29092 * @event OverlayViewDraw
29093 * Fires when OverlayView Draw
29094 * @param {Roo.bootstrap.LocationPicker} this
29096 OverlayViewDraw : true,
29098 * @event OverlayViewOnAdd
29099 * Fires when OverlayView Draw
29100 * @param {Roo.bootstrap.LocationPicker} this
29102 OverlayViewOnAdd : true,
29104 * @event OverlayViewOnRemove
29105 * Fires when OverlayView Draw
29106 * @param {Roo.bootstrap.LocationPicker} this
29108 OverlayViewOnRemove : true,
29110 * @event OverlayViewShow
29111 * Fires when OverlayView Draw
29112 * @param {Roo.bootstrap.LocationPicker} this
29113 * @param {Pixel} cpx
29115 OverlayViewShow : true,
29117 * @event OverlayViewHide
29118 * Fires when OverlayView Draw
29119 * @param {Roo.bootstrap.LocationPicker} this
29121 OverlayViewHide : true,
29123 * @event loadexception
29124 * Fires when load google lib failed.
29125 * @param {Roo.bootstrap.LocationPicker} this
29127 loadexception : true
29132 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29134 gMapContext: false,
29140 mapTypeControl: false,
29141 disableDoubleClickZoom: false,
29143 streetViewControl: false,
29147 enableAutocomplete: false,
29148 enableReverseGeocode: true,
29151 getAutoCreate: function()
29156 cls: 'roo-location-picker'
29162 initEvents: function(ct, position)
29164 if(!this.el.getWidth() || this.isApplied()){
29168 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29173 initial: function()
29175 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29176 this.fireEvent('loadexception', this);
29180 if(!this.mapTypeId){
29181 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29184 this.gMapContext = this.GMapContext();
29186 this.initOverlayView();
29188 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29192 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29193 _this.setPosition(_this.gMapContext.marker.position);
29196 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29197 _this.fireEvent('mapClick', this, event);
29201 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29202 _this.fireEvent('mapRightClick', this, event);
29206 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29207 _this.fireEvent('markerClick', this, event);
29211 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29212 _this.fireEvent('markerRightClick', this, event);
29216 this.setPosition(this.gMapContext.location);
29218 this.fireEvent('initial', this, this.gMapContext.location);
29221 initOverlayView: function()
29225 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29229 _this.fireEvent('OverlayViewDraw', _this);
29234 _this.fireEvent('OverlayViewOnAdd', _this);
29237 onRemove: function()
29239 _this.fireEvent('OverlayViewOnRemove', _this);
29242 show: function(cpx)
29244 _this.fireEvent('OverlayViewShow', _this, cpx);
29249 _this.fireEvent('OverlayViewHide', _this);
29255 fromLatLngToContainerPixel: function(event)
29257 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29260 isApplied: function()
29262 return this.getGmapContext() == false ? false : true;
29265 getGmapContext: function()
29267 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29270 GMapContext: function()
29272 var position = new google.maps.LatLng(this.latitude, this.longitude);
29274 var _map = new google.maps.Map(this.el.dom, {
29277 mapTypeId: this.mapTypeId,
29278 mapTypeControl: this.mapTypeControl,
29279 disableDoubleClickZoom: this.disableDoubleClickZoom,
29280 scrollwheel: this.scrollwheel,
29281 streetViewControl: this.streetViewControl,
29282 locationName: this.locationName,
29283 draggable: this.draggable,
29284 enableAutocomplete: this.enableAutocomplete,
29285 enableReverseGeocode: this.enableReverseGeocode
29288 var _marker = new google.maps.Marker({
29289 position: position,
29291 title: this.markerTitle,
29292 draggable: this.draggable
29299 location: position,
29300 radius: this.radius,
29301 locationName: this.locationName,
29302 addressComponents: {
29303 formatted_address: null,
29304 addressLine1: null,
29305 addressLine2: null,
29307 streetNumber: null,
29311 stateOrProvince: null
29314 domContainer: this.el.dom,
29315 geodecoder: new google.maps.Geocoder()
29319 drawCircle: function(center, radius, options)
29321 if (this.gMapContext.circle != null) {
29322 this.gMapContext.circle.setMap(null);
29326 options = Roo.apply({}, options, {
29327 strokeColor: "#0000FF",
29328 strokeOpacity: .35,
29330 fillColor: "#0000FF",
29334 options.map = this.gMapContext.map;
29335 options.radius = radius;
29336 options.center = center;
29337 this.gMapContext.circle = new google.maps.Circle(options);
29338 return this.gMapContext.circle;
29344 setPosition: function(location)
29346 this.gMapContext.location = location;
29347 this.gMapContext.marker.setPosition(location);
29348 this.gMapContext.map.panTo(location);
29349 this.drawCircle(location, this.gMapContext.radius, {});
29353 if (this.gMapContext.settings.enableReverseGeocode) {
29354 this.gMapContext.geodecoder.geocode({
29355 latLng: this.gMapContext.location
29356 }, function(results, status) {
29358 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29359 _this.gMapContext.locationName = results[0].formatted_address;
29360 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29362 _this.fireEvent('positionchanged', this, location);
29369 this.fireEvent('positionchanged', this, location);
29374 google.maps.event.trigger(this.gMapContext.map, "resize");
29376 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29378 this.fireEvent('resize', this);
29381 setPositionByLatLng: function(latitude, longitude)
29383 this.setPosition(new google.maps.LatLng(latitude, longitude));
29386 getCurrentPosition: function()
29389 latitude: this.gMapContext.location.lat(),
29390 longitude: this.gMapContext.location.lng()
29394 getAddressName: function()
29396 return this.gMapContext.locationName;
29399 getAddressComponents: function()
29401 return this.gMapContext.addressComponents;
29404 address_component_from_google_geocode: function(address_components)
29408 for (var i = 0; i < address_components.length; i++) {
29409 var component = address_components[i];
29410 if (component.types.indexOf("postal_code") >= 0) {
29411 result.postalCode = component.short_name;
29412 } else if (component.types.indexOf("street_number") >= 0) {
29413 result.streetNumber = component.short_name;
29414 } else if (component.types.indexOf("route") >= 0) {
29415 result.streetName = component.short_name;
29416 } else if (component.types.indexOf("neighborhood") >= 0) {
29417 result.city = component.short_name;
29418 } else if (component.types.indexOf("locality") >= 0) {
29419 result.city = component.short_name;
29420 } else if (component.types.indexOf("sublocality") >= 0) {
29421 result.district = component.short_name;
29422 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29423 result.stateOrProvince = component.short_name;
29424 } else if (component.types.indexOf("country") >= 0) {
29425 result.country = component.short_name;
29429 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29430 result.addressLine2 = "";
29434 setZoomLevel: function(zoom)
29436 this.gMapContext.map.setZoom(zoom);
29449 this.fireEvent('show', this);
29460 this.fireEvent('hide', this);
29465 Roo.apply(Roo.bootstrap.LocationPicker, {
29467 OverlayView : function(map, options)
29469 options = options || {};
29476 * @class Roo.bootstrap.Alert
29477 * @extends Roo.bootstrap.Component
29478 * Bootstrap Alert class - shows an alert area box
29480 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29481 Enter a valid email address
29484 * @cfg {String} title The title of alert
29485 * @cfg {String} html The content of alert
29486 * @cfg {String} weight ( success | info | warning | danger )
29487 * @cfg {String} faicon font-awesomeicon
29490 * Create a new alert
29491 * @param {Object} config The config object
29495 Roo.bootstrap.Alert = function(config){
29496 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29500 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29507 getAutoCreate : function()
29516 cls : 'roo-alert-icon'
29521 cls : 'roo-alert-title',
29526 cls : 'roo-alert-text',
29533 cfg.cn[0].cls += ' fa ' + this.faicon;
29537 cfg.cls += ' alert-' + this.weight;
29543 initEvents: function()
29545 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29548 setTitle : function(str)
29550 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29553 setText : function(str)
29555 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29558 setWeight : function(weight)
29561 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29564 this.weight = weight;
29566 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29569 setIcon : function(icon)
29572 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29575 this.faicon = icon;
29577 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29598 * @class Roo.bootstrap.UploadCropbox
29599 * @extends Roo.bootstrap.Component
29600 * Bootstrap UploadCropbox class
29601 * @cfg {String} emptyText show when image has been loaded
29602 * @cfg {String} rotateNotify show when image too small to rotate
29603 * @cfg {Number} errorTimeout default 3000
29604 * @cfg {Number} minWidth default 300
29605 * @cfg {Number} minHeight default 300
29606 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29607 * @cfg {Boolean} isDocument (true|false) default false
29608 * @cfg {String} url action url
29609 * @cfg {String} paramName default 'imageUpload'
29610 * @cfg {String} method default POST
29611 * @cfg {Boolean} loadMask (true|false) default true
29612 * @cfg {Boolean} loadingText default 'Loading...'
29615 * Create a new UploadCropbox
29616 * @param {Object} config The config object
29619 Roo.bootstrap.UploadCropbox = function(config){
29620 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29624 * @event beforeselectfile
29625 * Fire before select file
29626 * @param {Roo.bootstrap.UploadCropbox} this
29628 "beforeselectfile" : true,
29631 * Fire after initEvent
29632 * @param {Roo.bootstrap.UploadCropbox} this
29637 * Fire after initEvent
29638 * @param {Roo.bootstrap.UploadCropbox} this
29639 * @param {String} data
29644 * Fire when preparing the file data
29645 * @param {Roo.bootstrap.UploadCropbox} this
29646 * @param {Object} file
29651 * Fire when get exception
29652 * @param {Roo.bootstrap.UploadCropbox} this
29653 * @param {XMLHttpRequest} xhr
29655 "exception" : true,
29657 * @event beforeloadcanvas
29658 * Fire before load the canvas
29659 * @param {Roo.bootstrap.UploadCropbox} this
29660 * @param {String} src
29662 "beforeloadcanvas" : true,
29665 * Fire when trash image
29666 * @param {Roo.bootstrap.UploadCropbox} this
29671 * Fire when download the image
29672 * @param {Roo.bootstrap.UploadCropbox} this
29676 * @event footerbuttonclick
29677 * Fire when footerbuttonclick
29678 * @param {Roo.bootstrap.UploadCropbox} this
29679 * @param {String} type
29681 "footerbuttonclick" : true,
29685 * @param {Roo.bootstrap.UploadCropbox} this
29690 * Fire when rotate the image
29691 * @param {Roo.bootstrap.UploadCropbox} this
29692 * @param {String} pos
29697 * Fire when inspect the file
29698 * @param {Roo.bootstrap.UploadCropbox} this
29699 * @param {Object} file
29704 * Fire when xhr upload the file
29705 * @param {Roo.bootstrap.UploadCropbox} this
29706 * @param {Object} data
29711 * Fire when arrange the file data
29712 * @param {Roo.bootstrap.UploadCropbox} this
29713 * @param {Object} formData
29718 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29721 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29723 emptyText : 'Click to upload image',
29724 rotateNotify : 'Image is too small to rotate',
29725 errorTimeout : 3000,
29739 cropType : 'image/jpeg',
29741 canvasLoaded : false,
29742 isDocument : false,
29744 paramName : 'imageUpload',
29746 loadingText : 'Loading...',
29749 getAutoCreate : function()
29753 cls : 'roo-upload-cropbox',
29757 cls : 'roo-upload-cropbox-selector',
29762 cls : 'roo-upload-cropbox-body',
29763 style : 'cursor:pointer',
29767 cls : 'roo-upload-cropbox-preview'
29771 cls : 'roo-upload-cropbox-thumb'
29775 cls : 'roo-upload-cropbox-empty-notify',
29776 html : this.emptyText
29780 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29781 html : this.rotateNotify
29787 cls : 'roo-upload-cropbox-footer',
29790 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29800 onRender : function(ct, position)
29802 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29804 if (this.buttons.length) {
29806 Roo.each(this.buttons, function(bb) {
29808 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29810 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29816 this.maskEl = this.el;
29820 initEvents : function()
29822 this.urlAPI = (window.createObjectURL && window) ||
29823 (window.URL && URL.revokeObjectURL && URL) ||
29824 (window.webkitURL && webkitURL);
29826 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29827 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29829 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29830 this.selectorEl.hide();
29832 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29833 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29835 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29836 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29837 this.thumbEl.hide();
29839 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29840 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29842 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29843 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29844 this.errorEl.hide();
29846 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29847 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29848 this.footerEl.hide();
29850 this.setThumbBoxSize();
29856 this.fireEvent('initial', this);
29863 window.addEventListener("resize", function() { _this.resize(); } );
29865 this.bodyEl.on('click', this.beforeSelectFile, this);
29868 this.bodyEl.on('touchstart', this.onTouchStart, this);
29869 this.bodyEl.on('touchmove', this.onTouchMove, this);
29870 this.bodyEl.on('touchend', this.onTouchEnd, this);
29874 this.bodyEl.on('mousedown', this.onMouseDown, this);
29875 this.bodyEl.on('mousemove', this.onMouseMove, this);
29876 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29877 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29878 Roo.get(document).on('mouseup', this.onMouseUp, this);
29881 this.selectorEl.on('change', this.onFileSelected, this);
29887 this.baseScale = 1;
29889 this.baseRotate = 1;
29890 this.dragable = false;
29891 this.pinching = false;
29894 this.cropData = false;
29895 this.notifyEl.dom.innerHTML = this.emptyText;
29897 this.selectorEl.dom.value = '';
29901 resize : function()
29903 if(this.fireEvent('resize', this) != false){
29904 this.setThumbBoxPosition();
29905 this.setCanvasPosition();
29909 onFooterButtonClick : function(e, el, o, type)
29912 case 'rotate-left' :
29913 this.onRotateLeft(e);
29915 case 'rotate-right' :
29916 this.onRotateRight(e);
29919 this.beforeSelectFile(e);
29934 this.fireEvent('footerbuttonclick', this, type);
29937 beforeSelectFile : function(e)
29939 e.preventDefault();
29941 if(this.fireEvent('beforeselectfile', this) != false){
29942 this.selectorEl.dom.click();
29946 onFileSelected : function(e)
29948 e.preventDefault();
29950 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29954 var file = this.selectorEl.dom.files[0];
29956 if(this.fireEvent('inspect', this, file) != false){
29957 this.prepare(file);
29962 trash : function(e)
29964 this.fireEvent('trash', this);
29967 download : function(e)
29969 this.fireEvent('download', this);
29972 loadCanvas : function(src)
29974 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29978 this.imageEl = document.createElement('img');
29982 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29984 this.imageEl.src = src;
29988 onLoadCanvas : function()
29990 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29991 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29993 this.bodyEl.un('click', this.beforeSelectFile, this);
29995 this.notifyEl.hide();
29996 this.thumbEl.show();
29997 this.footerEl.show();
29999 this.baseRotateLevel();
30001 if(this.isDocument){
30002 this.setThumbBoxSize();
30005 this.setThumbBoxPosition();
30007 this.baseScaleLevel();
30013 this.canvasLoaded = true;
30016 this.maskEl.unmask();
30021 setCanvasPosition : function()
30023 if(!this.canvasEl){
30027 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30028 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30030 this.previewEl.setLeft(pw);
30031 this.previewEl.setTop(ph);
30035 onMouseDown : function(e)
30039 this.dragable = true;
30040 this.pinching = false;
30042 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30043 this.dragable = false;
30047 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30048 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30052 onMouseMove : function(e)
30056 if(!this.canvasLoaded){
30060 if (!this.dragable){
30064 var minX = Math.ceil(this.thumbEl.getLeft(true));
30065 var minY = Math.ceil(this.thumbEl.getTop(true));
30067 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30068 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30070 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30071 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30073 x = x - this.mouseX;
30074 y = y - this.mouseY;
30076 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30077 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30079 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30080 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30082 this.previewEl.setLeft(bgX);
30083 this.previewEl.setTop(bgY);
30085 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30086 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30089 onMouseUp : function(e)
30093 this.dragable = false;
30096 onMouseWheel : function(e)
30100 this.startScale = this.scale;
30102 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30104 if(!this.zoomable()){
30105 this.scale = this.startScale;
30114 zoomable : function()
30116 var minScale = this.thumbEl.getWidth() / this.minWidth;
30118 if(this.minWidth < this.minHeight){
30119 minScale = this.thumbEl.getHeight() / this.minHeight;
30122 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30123 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30127 (this.rotate == 0 || this.rotate == 180) &&
30129 width > this.imageEl.OriginWidth ||
30130 height > this.imageEl.OriginHeight ||
30131 (width < this.minWidth && height < this.minHeight)
30139 (this.rotate == 90 || this.rotate == 270) &&
30141 width > this.imageEl.OriginWidth ||
30142 height > this.imageEl.OriginHeight ||
30143 (width < this.minHeight && height < this.minWidth)
30150 !this.isDocument &&
30151 (this.rotate == 0 || this.rotate == 180) &&
30153 width < this.minWidth ||
30154 width > this.imageEl.OriginWidth ||
30155 height < this.minHeight ||
30156 height > this.imageEl.OriginHeight
30163 !this.isDocument &&
30164 (this.rotate == 90 || this.rotate == 270) &&
30166 width < this.minHeight ||
30167 width > this.imageEl.OriginWidth ||
30168 height < this.minWidth ||
30169 height > this.imageEl.OriginHeight
30179 onRotateLeft : function(e)
30181 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30183 var minScale = this.thumbEl.getWidth() / this.minWidth;
30185 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30186 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30188 this.startScale = this.scale;
30190 while (this.getScaleLevel() < minScale){
30192 this.scale = this.scale + 1;
30194 if(!this.zoomable()){
30199 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30200 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30205 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30212 this.scale = this.startScale;
30214 this.onRotateFail();
30219 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30221 if(this.isDocument){
30222 this.setThumbBoxSize();
30223 this.setThumbBoxPosition();
30224 this.setCanvasPosition();
30229 this.fireEvent('rotate', this, 'left');
30233 onRotateRight : function(e)
30235 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30237 var minScale = this.thumbEl.getWidth() / this.minWidth;
30239 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30240 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30242 this.startScale = this.scale;
30244 while (this.getScaleLevel() < minScale){
30246 this.scale = this.scale + 1;
30248 if(!this.zoomable()){
30253 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30254 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30259 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30266 this.scale = this.startScale;
30268 this.onRotateFail();
30273 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30275 if(this.isDocument){
30276 this.setThumbBoxSize();
30277 this.setThumbBoxPosition();
30278 this.setCanvasPosition();
30283 this.fireEvent('rotate', this, 'right');
30286 onRotateFail : function()
30288 this.errorEl.show(true);
30292 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30297 this.previewEl.dom.innerHTML = '';
30299 var canvasEl = document.createElement("canvas");
30301 var contextEl = canvasEl.getContext("2d");
30303 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30304 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30305 var center = this.imageEl.OriginWidth / 2;
30307 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30308 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30309 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30310 center = this.imageEl.OriginHeight / 2;
30313 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30315 contextEl.translate(center, center);
30316 contextEl.rotate(this.rotate * Math.PI / 180);
30318 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30320 this.canvasEl = document.createElement("canvas");
30322 this.contextEl = this.canvasEl.getContext("2d");
30324 switch (this.rotate) {
30327 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30328 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30330 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30335 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30336 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30338 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30339 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);
30343 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30348 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30349 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30351 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30352 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);
30356 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);
30361 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30362 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30364 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30365 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30369 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);
30376 this.previewEl.appendChild(this.canvasEl);
30378 this.setCanvasPosition();
30383 if(!this.canvasLoaded){
30387 var imageCanvas = document.createElement("canvas");
30389 var imageContext = imageCanvas.getContext("2d");
30391 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30392 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30394 var center = imageCanvas.width / 2;
30396 imageContext.translate(center, center);
30398 imageContext.rotate(this.rotate * Math.PI / 180);
30400 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30402 var canvas = document.createElement("canvas");
30404 var context = canvas.getContext("2d");
30406 canvas.width = this.minWidth;
30407 canvas.height = this.minHeight;
30409 switch (this.rotate) {
30412 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30413 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30415 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30416 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30418 var targetWidth = this.minWidth - 2 * x;
30419 var targetHeight = this.minHeight - 2 * y;
30423 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30424 scale = targetWidth / width;
30427 if(x > 0 && y == 0){
30428 scale = targetHeight / height;
30431 if(x > 0 && y > 0){
30432 scale = targetWidth / width;
30434 if(width < height){
30435 scale = targetHeight / height;
30439 context.scale(scale, scale);
30441 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30442 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30444 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30445 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30447 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30452 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30453 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30455 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30456 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30458 var targetWidth = this.minWidth - 2 * x;
30459 var targetHeight = this.minHeight - 2 * y;
30463 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30464 scale = targetWidth / width;
30467 if(x > 0 && y == 0){
30468 scale = targetHeight / height;
30471 if(x > 0 && y > 0){
30472 scale = targetWidth / width;
30474 if(width < height){
30475 scale = targetHeight / height;
30479 context.scale(scale, scale);
30481 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30482 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30484 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30485 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30487 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30489 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30494 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30495 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30497 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30498 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30500 var targetWidth = this.minWidth - 2 * x;
30501 var targetHeight = this.minHeight - 2 * y;
30505 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30506 scale = targetWidth / width;
30509 if(x > 0 && y == 0){
30510 scale = targetHeight / height;
30513 if(x > 0 && y > 0){
30514 scale = targetWidth / width;
30516 if(width < height){
30517 scale = targetHeight / height;
30521 context.scale(scale, scale);
30523 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30524 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30526 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30527 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30529 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30530 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30532 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30537 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30538 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30540 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30541 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30543 var targetWidth = this.minWidth - 2 * x;
30544 var targetHeight = this.minHeight - 2 * y;
30548 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30549 scale = targetWidth / width;
30552 if(x > 0 && y == 0){
30553 scale = targetHeight / height;
30556 if(x > 0 && y > 0){
30557 scale = targetWidth / width;
30559 if(width < height){
30560 scale = targetHeight / height;
30564 context.scale(scale, scale);
30566 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30567 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30569 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30570 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30572 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30574 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30581 this.cropData = canvas.toDataURL(this.cropType);
30583 if(this.fireEvent('crop', this, this.cropData) !== false){
30584 this.process(this.file, this.cropData);
30591 setThumbBoxSize : function()
30595 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30596 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30597 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30599 this.minWidth = width;
30600 this.minHeight = height;
30602 if(this.rotate == 90 || this.rotate == 270){
30603 this.minWidth = height;
30604 this.minHeight = width;
30609 width = Math.ceil(this.minWidth * height / this.minHeight);
30611 if(this.minWidth > this.minHeight){
30613 height = Math.ceil(this.minHeight * width / this.minWidth);
30616 this.thumbEl.setStyle({
30617 width : width + 'px',
30618 height : height + 'px'
30625 setThumbBoxPosition : function()
30627 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30628 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30630 this.thumbEl.setLeft(x);
30631 this.thumbEl.setTop(y);
30635 baseRotateLevel : function()
30637 this.baseRotate = 1;
30640 typeof(this.exif) != 'undefined' &&
30641 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30642 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30644 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30647 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30651 baseScaleLevel : function()
30655 if(this.isDocument){
30657 if(this.baseRotate == 6 || this.baseRotate == 8){
30659 height = this.thumbEl.getHeight();
30660 this.baseScale = height / this.imageEl.OriginWidth;
30662 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30663 width = this.thumbEl.getWidth();
30664 this.baseScale = width / this.imageEl.OriginHeight;
30670 height = this.thumbEl.getHeight();
30671 this.baseScale = height / this.imageEl.OriginHeight;
30673 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30674 width = this.thumbEl.getWidth();
30675 this.baseScale = width / this.imageEl.OriginWidth;
30681 if(this.baseRotate == 6 || this.baseRotate == 8){
30683 width = this.thumbEl.getHeight();
30684 this.baseScale = width / this.imageEl.OriginHeight;
30686 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30687 height = this.thumbEl.getWidth();
30688 this.baseScale = height / this.imageEl.OriginHeight;
30691 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30692 height = this.thumbEl.getWidth();
30693 this.baseScale = height / this.imageEl.OriginHeight;
30695 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30696 width = this.thumbEl.getHeight();
30697 this.baseScale = width / this.imageEl.OriginWidth;
30704 width = this.thumbEl.getWidth();
30705 this.baseScale = width / this.imageEl.OriginWidth;
30707 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30708 height = this.thumbEl.getHeight();
30709 this.baseScale = height / this.imageEl.OriginHeight;
30712 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30714 height = this.thumbEl.getHeight();
30715 this.baseScale = height / this.imageEl.OriginHeight;
30717 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30718 width = this.thumbEl.getWidth();
30719 this.baseScale = width / this.imageEl.OriginWidth;
30727 getScaleLevel : function()
30729 return this.baseScale * Math.pow(1.1, this.scale);
30732 onTouchStart : function(e)
30734 if(!this.canvasLoaded){
30735 this.beforeSelectFile(e);
30739 var touches = e.browserEvent.touches;
30745 if(touches.length == 1){
30746 this.onMouseDown(e);
30750 if(touches.length != 2){
30756 for(var i = 0, finger; finger = touches[i]; i++){
30757 coords.push(finger.pageX, finger.pageY);
30760 var x = Math.pow(coords[0] - coords[2], 2);
30761 var y = Math.pow(coords[1] - coords[3], 2);
30763 this.startDistance = Math.sqrt(x + y);
30765 this.startScale = this.scale;
30767 this.pinching = true;
30768 this.dragable = false;
30772 onTouchMove : function(e)
30774 if(!this.pinching && !this.dragable){
30778 var touches = e.browserEvent.touches;
30785 this.onMouseMove(e);
30791 for(var i = 0, finger; finger = touches[i]; i++){
30792 coords.push(finger.pageX, finger.pageY);
30795 var x = Math.pow(coords[0] - coords[2], 2);
30796 var y = Math.pow(coords[1] - coords[3], 2);
30798 this.endDistance = Math.sqrt(x + y);
30800 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30802 if(!this.zoomable()){
30803 this.scale = this.startScale;
30811 onTouchEnd : function(e)
30813 this.pinching = false;
30814 this.dragable = false;
30818 process : function(file, crop)
30821 this.maskEl.mask(this.loadingText);
30824 this.xhr = new XMLHttpRequest();
30826 file.xhr = this.xhr;
30828 this.xhr.open(this.method, this.url, true);
30831 "Accept": "application/json",
30832 "Cache-Control": "no-cache",
30833 "X-Requested-With": "XMLHttpRequest"
30836 for (var headerName in headers) {
30837 var headerValue = headers[headerName];
30839 this.xhr.setRequestHeader(headerName, headerValue);
30845 this.xhr.onload = function()
30847 _this.xhrOnLoad(_this.xhr);
30850 this.xhr.onerror = function()
30852 _this.xhrOnError(_this.xhr);
30855 var formData = new FormData();
30857 formData.append('returnHTML', 'NO');
30860 formData.append('crop', crop);
30863 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30864 formData.append(this.paramName, file, file.name);
30867 if(typeof(file.filename) != 'undefined'){
30868 formData.append('filename', file.filename);
30871 if(typeof(file.mimetype) != 'undefined'){
30872 formData.append('mimetype', file.mimetype);
30875 if(this.fireEvent('arrange', this, formData) != false){
30876 this.xhr.send(formData);
30880 xhrOnLoad : function(xhr)
30883 this.maskEl.unmask();
30886 if (xhr.readyState !== 4) {
30887 this.fireEvent('exception', this, xhr);
30891 var response = Roo.decode(xhr.responseText);
30893 if(!response.success){
30894 this.fireEvent('exception', this, xhr);
30898 var response = Roo.decode(xhr.responseText);
30900 this.fireEvent('upload', this, response);
30904 xhrOnError : function()
30907 this.maskEl.unmask();
30910 Roo.log('xhr on error');
30912 var response = Roo.decode(xhr.responseText);
30918 prepare : function(file)
30921 this.maskEl.mask(this.loadingText);
30927 if(typeof(file) === 'string'){
30928 this.loadCanvas(file);
30932 if(!file || !this.urlAPI){
30937 this.cropType = file.type;
30941 if(this.fireEvent('prepare', this, this.file) != false){
30943 var reader = new FileReader();
30945 reader.onload = function (e) {
30946 if (e.target.error) {
30947 Roo.log(e.target.error);
30951 var buffer = e.target.result,
30952 dataView = new DataView(buffer),
30954 maxOffset = dataView.byteLength - 4,
30958 if (dataView.getUint16(0) === 0xffd8) {
30959 while (offset < maxOffset) {
30960 markerBytes = dataView.getUint16(offset);
30962 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30963 markerLength = dataView.getUint16(offset + 2) + 2;
30964 if (offset + markerLength > dataView.byteLength) {
30965 Roo.log('Invalid meta data: Invalid segment size.');
30969 if(markerBytes == 0xffe1){
30970 _this.parseExifData(
30977 offset += markerLength;
30987 var url = _this.urlAPI.createObjectURL(_this.file);
30989 _this.loadCanvas(url);
30994 reader.readAsArrayBuffer(this.file);
31000 parseExifData : function(dataView, offset, length)
31002 var tiffOffset = offset + 10,
31006 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31007 // No Exif data, might be XMP data instead
31011 // Check for the ASCII code for "Exif" (0x45786966):
31012 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31013 // No Exif data, might be XMP data instead
31016 if (tiffOffset + 8 > dataView.byteLength) {
31017 Roo.log('Invalid Exif data: Invalid segment size.');
31020 // Check for the two null bytes:
31021 if (dataView.getUint16(offset + 8) !== 0x0000) {
31022 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31025 // Check the byte alignment:
31026 switch (dataView.getUint16(tiffOffset)) {
31028 littleEndian = true;
31031 littleEndian = false;
31034 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31037 // Check for the TIFF tag marker (0x002A):
31038 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31039 Roo.log('Invalid Exif data: Missing TIFF marker.');
31042 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31043 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31045 this.parseExifTags(
31048 tiffOffset + dirOffset,
31053 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31058 if (dirOffset + 6 > dataView.byteLength) {
31059 Roo.log('Invalid Exif data: Invalid directory offset.');
31062 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31063 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31064 if (dirEndOffset + 4 > dataView.byteLength) {
31065 Roo.log('Invalid Exif data: Invalid directory size.');
31068 for (i = 0; i < tagsNumber; i += 1) {
31072 dirOffset + 2 + 12 * i, // tag offset
31076 // Return the offset to the next directory:
31077 return dataView.getUint32(dirEndOffset, littleEndian);
31080 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31082 var tag = dataView.getUint16(offset, littleEndian);
31084 this.exif[tag] = this.getExifValue(
31088 dataView.getUint16(offset + 2, littleEndian), // tag type
31089 dataView.getUint32(offset + 4, littleEndian), // tag length
31094 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31096 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31105 Roo.log('Invalid Exif data: Invalid tag type.');
31109 tagSize = tagType.size * length;
31110 // Determine if the value is contained in the dataOffset bytes,
31111 // or if the value at the dataOffset is a pointer to the actual data:
31112 dataOffset = tagSize > 4 ?
31113 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31114 if (dataOffset + tagSize > dataView.byteLength) {
31115 Roo.log('Invalid Exif data: Invalid data offset.');
31118 if (length === 1) {
31119 return tagType.getValue(dataView, dataOffset, littleEndian);
31122 for (i = 0; i < length; i += 1) {
31123 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31126 if (tagType.ascii) {
31128 // Concatenate the chars:
31129 for (i = 0; i < values.length; i += 1) {
31131 // Ignore the terminating NULL byte(s):
31132 if (c === '\u0000') {
31144 Roo.apply(Roo.bootstrap.UploadCropbox, {
31146 'Orientation': 0x0112
31150 1: 0, //'top-left',
31152 3: 180, //'bottom-right',
31153 // 4: 'bottom-left',
31155 6: 90, //'right-top',
31156 // 7: 'right-bottom',
31157 8: 270 //'left-bottom'
31161 // byte, 8-bit unsigned int:
31163 getValue: function (dataView, dataOffset) {
31164 return dataView.getUint8(dataOffset);
31168 // ascii, 8-bit byte:
31170 getValue: function (dataView, dataOffset) {
31171 return String.fromCharCode(dataView.getUint8(dataOffset));
31176 // short, 16 bit int:
31178 getValue: function (dataView, dataOffset, littleEndian) {
31179 return dataView.getUint16(dataOffset, littleEndian);
31183 // long, 32 bit int:
31185 getValue: function (dataView, dataOffset, littleEndian) {
31186 return dataView.getUint32(dataOffset, littleEndian);
31190 // rational = two long values, first is numerator, second is denominator:
31192 getValue: function (dataView, dataOffset, littleEndian) {
31193 return dataView.getUint32(dataOffset, littleEndian) /
31194 dataView.getUint32(dataOffset + 4, littleEndian);
31198 // slong, 32 bit signed int:
31200 getValue: function (dataView, dataOffset, littleEndian) {
31201 return dataView.getInt32(dataOffset, littleEndian);
31205 // srational, two slongs, first is numerator, second is denominator:
31207 getValue: function (dataView, dataOffset, littleEndian) {
31208 return dataView.getInt32(dataOffset, littleEndian) /
31209 dataView.getInt32(dataOffset + 4, littleEndian);
31219 cls : 'btn-group roo-upload-cropbox-rotate-left',
31220 action : 'rotate-left',
31224 cls : 'btn btn-default',
31225 html : '<i class="fa fa-undo"></i>'
31231 cls : 'btn-group roo-upload-cropbox-picture',
31232 action : 'picture',
31236 cls : 'btn btn-default',
31237 html : '<i class="fa fa-picture-o"></i>'
31243 cls : 'btn-group roo-upload-cropbox-rotate-right',
31244 action : 'rotate-right',
31248 cls : 'btn btn-default',
31249 html : '<i class="fa fa-repeat"></i>'
31257 cls : 'btn-group roo-upload-cropbox-rotate-left',
31258 action : 'rotate-left',
31262 cls : 'btn btn-default',
31263 html : '<i class="fa fa-undo"></i>'
31269 cls : 'btn-group roo-upload-cropbox-download',
31270 action : 'download',
31274 cls : 'btn btn-default',
31275 html : '<i class="fa fa-download"></i>'
31281 cls : 'btn-group roo-upload-cropbox-crop',
31286 cls : 'btn btn-default',
31287 html : '<i class="fa fa-crop"></i>'
31293 cls : 'btn-group roo-upload-cropbox-trash',
31298 cls : 'btn btn-default',
31299 html : '<i class="fa fa-trash"></i>'
31305 cls : 'btn-group roo-upload-cropbox-rotate-right',
31306 action : 'rotate-right',
31310 cls : 'btn btn-default',
31311 html : '<i class="fa fa-repeat"></i>'
31319 cls : 'btn-group roo-upload-cropbox-rotate-left',
31320 action : 'rotate-left',
31324 cls : 'btn btn-default',
31325 html : '<i class="fa fa-undo"></i>'
31331 cls : 'btn-group roo-upload-cropbox-rotate-right',
31332 action : 'rotate-right',
31336 cls : 'btn btn-default',
31337 html : '<i class="fa fa-repeat"></i>'
31350 * @class Roo.bootstrap.DocumentManager
31351 * @extends Roo.bootstrap.Component
31352 * Bootstrap DocumentManager class
31353 * @cfg {String} paramName default 'imageUpload'
31354 * @cfg {String} toolTipName default 'filename'
31355 * @cfg {String} method default POST
31356 * @cfg {String} url action url
31357 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31358 * @cfg {Boolean} multiple multiple upload default true
31359 * @cfg {Number} thumbSize default 300
31360 * @cfg {String} fieldLabel
31361 * @cfg {Number} labelWidth default 4
31362 * @cfg {String} labelAlign (left|top) default left
31363 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31364 * @cfg {Number} labellg set the width of label (1-12)
31365 * @cfg {Number} labelmd set the width of label (1-12)
31366 * @cfg {Number} labelsm set the width of label (1-12)
31367 * @cfg {Number} labelxs set the width of label (1-12)
31370 * Create a new DocumentManager
31371 * @param {Object} config The config object
31374 Roo.bootstrap.DocumentManager = function(config){
31375 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31378 this.delegates = [];
31383 * Fire when initial the DocumentManager
31384 * @param {Roo.bootstrap.DocumentManager} this
31389 * inspect selected file
31390 * @param {Roo.bootstrap.DocumentManager} this
31391 * @param {File} file
31396 * Fire when xhr load exception
31397 * @param {Roo.bootstrap.DocumentManager} this
31398 * @param {XMLHttpRequest} xhr
31400 "exception" : true,
31402 * @event afterupload
31403 * Fire when xhr load exception
31404 * @param {Roo.bootstrap.DocumentManager} this
31405 * @param {XMLHttpRequest} xhr
31407 "afterupload" : true,
31410 * prepare the form data
31411 * @param {Roo.bootstrap.DocumentManager} this
31412 * @param {Object} formData
31417 * Fire when remove the file
31418 * @param {Roo.bootstrap.DocumentManager} this
31419 * @param {Object} file
31424 * Fire after refresh the file
31425 * @param {Roo.bootstrap.DocumentManager} this
31430 * Fire after click the image
31431 * @param {Roo.bootstrap.DocumentManager} this
31432 * @param {Object} file
31437 * Fire when upload a image and editable set to true
31438 * @param {Roo.bootstrap.DocumentManager} this
31439 * @param {Object} file
31443 * @event beforeselectfile
31444 * Fire before select file
31445 * @param {Roo.bootstrap.DocumentManager} this
31447 "beforeselectfile" : true,
31450 * Fire before process file
31451 * @param {Roo.bootstrap.DocumentManager} this
31452 * @param {Object} file
31456 * @event previewrendered
31457 * Fire when preview rendered
31458 * @param {Roo.bootstrap.DocumentManager} this
31459 * @param {Object} file
31461 "previewrendered" : true,
31464 "previewResize" : true
31469 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31478 paramName : 'imageUpload',
31479 toolTipName : 'filename',
31482 labelAlign : 'left',
31492 getAutoCreate : function()
31494 var managerWidget = {
31496 cls : 'roo-document-manager',
31500 cls : 'roo-document-manager-selector',
31505 cls : 'roo-document-manager-uploader',
31509 cls : 'roo-document-manager-upload-btn',
31510 html : '<i class="fa fa-plus"></i>'
31521 cls : 'column col-md-12',
31526 if(this.fieldLabel.length){
31531 cls : 'column col-md-12',
31532 html : this.fieldLabel
31536 cls : 'column col-md-12',
31541 if(this.labelAlign == 'left'){
31546 html : this.fieldLabel
31555 if(this.labelWidth > 12){
31556 content[0].style = "width: " + this.labelWidth + 'px';
31559 if(this.labelWidth < 13 && this.labelmd == 0){
31560 this.labelmd = this.labelWidth;
31563 if(this.labellg > 0){
31564 content[0].cls += ' col-lg-' + this.labellg;
31565 content[1].cls += ' col-lg-' + (12 - this.labellg);
31568 if(this.labelmd > 0){
31569 content[0].cls += ' col-md-' + this.labelmd;
31570 content[1].cls += ' col-md-' + (12 - this.labelmd);
31573 if(this.labelsm > 0){
31574 content[0].cls += ' col-sm-' + this.labelsm;
31575 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31578 if(this.labelxs > 0){
31579 content[0].cls += ' col-xs-' + this.labelxs;
31580 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31588 cls : 'row clearfix',
31596 initEvents : function()
31598 this.managerEl = this.el.select('.roo-document-manager', true).first();
31599 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31601 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31602 this.selectorEl.hide();
31605 this.selectorEl.attr('multiple', 'multiple');
31608 this.selectorEl.on('change', this.onFileSelected, this);
31610 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31611 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31613 this.uploader.on('click', this.onUploaderClick, this);
31615 this.renderProgressDialog();
31619 window.addEventListener("resize", function() { _this.refresh(); } );
31621 this.fireEvent('initial', this);
31624 renderProgressDialog : function()
31628 this.progressDialog = new Roo.bootstrap.Modal({
31629 cls : 'roo-document-manager-progress-dialog',
31630 allow_close : false,
31641 btnclick : function() {
31642 _this.uploadCancel();
31648 this.progressDialog.render(Roo.get(document.body));
31650 this.progress = new Roo.bootstrap.Progress({
31651 cls : 'roo-document-manager-progress',
31656 this.progress.render(this.progressDialog.getChildContainer());
31658 this.progressBar = new Roo.bootstrap.ProgressBar({
31659 cls : 'roo-document-manager-progress-bar',
31662 aria_valuemax : 12,
31666 this.progressBar.render(this.progress.getChildContainer());
31669 onUploaderClick : function(e)
31671 e.preventDefault();
31673 if(this.fireEvent('beforeselectfile', this) != false){
31674 this.selectorEl.dom.click();
31679 onFileSelected : function(e)
31681 e.preventDefault();
31683 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31687 Roo.each(this.selectorEl.dom.files, function(file){
31688 if(this.fireEvent('inspect', this, file) != false){
31689 this.files.push(file);
31699 this.selectorEl.dom.value = '';
31701 if(!this.files || !this.files.length){
31705 if(this.boxes > 0 && this.files.length > this.boxes){
31706 this.files = this.files.slice(0, this.boxes);
31709 this.uploader.show();
31711 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31712 this.uploader.hide();
31721 Roo.each(this.files, function(file){
31723 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31724 var f = this.renderPreview(file);
31729 if(file.type.indexOf('image') != -1){
31730 this.delegates.push(
31732 _this.process(file);
31733 }).createDelegate(this)
31741 _this.process(file);
31742 }).createDelegate(this)
31747 this.files = files;
31749 this.delegates = this.delegates.concat(docs);
31751 if(!this.delegates.length){
31756 this.progressBar.aria_valuemax = this.delegates.length;
31763 arrange : function()
31765 if(!this.delegates.length){
31766 this.progressDialog.hide();
31771 var delegate = this.delegates.shift();
31773 this.progressDialog.show();
31775 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31777 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31782 refresh : function()
31784 this.uploader.show();
31786 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31787 this.uploader.hide();
31790 Roo.isTouch ? this.closable(false) : this.closable(true);
31792 this.fireEvent('refresh', this);
31795 onRemove : function(e, el, o)
31797 e.preventDefault();
31799 this.fireEvent('remove', this, o);
31803 remove : function(o)
31807 Roo.each(this.files, function(file){
31808 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31817 this.files = files;
31824 Roo.each(this.files, function(file){
31829 file.target.remove();
31838 onClick : function(e, el, o)
31840 e.preventDefault();
31842 this.fireEvent('click', this, o);
31846 closable : function(closable)
31848 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31850 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31862 xhrOnLoad : function(xhr)
31864 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31868 if (xhr.readyState !== 4) {
31870 this.fireEvent('exception', this, xhr);
31874 var response = Roo.decode(xhr.responseText);
31876 if(!response.success){
31878 this.fireEvent('exception', this, xhr);
31882 var file = this.renderPreview(response.data);
31884 this.files.push(file);
31888 this.fireEvent('afterupload', this, xhr);
31892 xhrOnError : function(xhr)
31894 Roo.log('xhr on error');
31896 var response = Roo.decode(xhr.responseText);
31903 process : function(file)
31905 if(this.fireEvent('process', this, file) !== false){
31906 if(this.editable && file.type.indexOf('image') != -1){
31907 this.fireEvent('edit', this, file);
31911 this.uploadStart(file, false);
31918 uploadStart : function(file, crop)
31920 this.xhr = new XMLHttpRequest();
31922 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31927 file.xhr = this.xhr;
31929 this.managerEl.createChild({
31931 cls : 'roo-document-manager-loading',
31935 tooltip : file.name,
31936 cls : 'roo-document-manager-thumb',
31937 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31943 this.xhr.open(this.method, this.url, true);
31946 "Accept": "application/json",
31947 "Cache-Control": "no-cache",
31948 "X-Requested-With": "XMLHttpRequest"
31951 for (var headerName in headers) {
31952 var headerValue = headers[headerName];
31954 this.xhr.setRequestHeader(headerName, headerValue);
31960 this.xhr.onload = function()
31962 _this.xhrOnLoad(_this.xhr);
31965 this.xhr.onerror = function()
31967 _this.xhrOnError(_this.xhr);
31970 var formData = new FormData();
31972 formData.append('returnHTML', 'NO');
31975 formData.append('crop', crop);
31978 formData.append(this.paramName, file, file.name);
31985 if(this.fireEvent('prepare', this, formData, options) != false){
31987 if(options.manually){
31991 this.xhr.send(formData);
31995 this.uploadCancel();
31998 uploadCancel : function()
32004 this.delegates = [];
32006 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32013 renderPreview : function(file)
32015 if(typeof(file.target) != 'undefined' && file.target){
32019 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32021 var previewEl = this.managerEl.createChild({
32023 cls : 'roo-document-manager-preview',
32027 tooltip : file[this.toolTipName],
32028 cls : 'roo-document-manager-thumb',
32029 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32034 html : '<i class="fa fa-times-circle"></i>'
32039 var close = previewEl.select('button.close', true).first();
32041 close.on('click', this.onRemove, this, file);
32043 file.target = previewEl;
32045 var image = previewEl.select('img', true).first();
32049 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32051 image.on('click', this.onClick, this, file);
32053 this.fireEvent('previewrendered', this, file);
32059 onPreviewLoad : function(file, image)
32061 if(typeof(file.target) == 'undefined' || !file.target){
32065 var width = image.dom.naturalWidth || image.dom.width;
32066 var height = image.dom.naturalHeight || image.dom.height;
32068 if(!this.previewResize) {
32072 if(width > height){
32073 file.target.addClass('wide');
32077 file.target.addClass('tall');
32082 uploadFromSource : function(file, crop)
32084 this.xhr = new XMLHttpRequest();
32086 this.managerEl.createChild({
32088 cls : 'roo-document-manager-loading',
32092 tooltip : file.name,
32093 cls : 'roo-document-manager-thumb',
32094 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32100 this.xhr.open(this.method, this.url, true);
32103 "Accept": "application/json",
32104 "Cache-Control": "no-cache",
32105 "X-Requested-With": "XMLHttpRequest"
32108 for (var headerName in headers) {
32109 var headerValue = headers[headerName];
32111 this.xhr.setRequestHeader(headerName, headerValue);
32117 this.xhr.onload = function()
32119 _this.xhrOnLoad(_this.xhr);
32122 this.xhr.onerror = function()
32124 _this.xhrOnError(_this.xhr);
32127 var formData = new FormData();
32129 formData.append('returnHTML', 'NO');
32131 formData.append('crop', crop);
32133 if(typeof(file.filename) != 'undefined'){
32134 formData.append('filename', file.filename);
32137 if(typeof(file.mimetype) != 'undefined'){
32138 formData.append('mimetype', file.mimetype);
32143 if(this.fireEvent('prepare', this, formData) != false){
32144 this.xhr.send(formData);
32154 * @class Roo.bootstrap.DocumentViewer
32155 * @extends Roo.bootstrap.Component
32156 * Bootstrap DocumentViewer class
32157 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32158 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32161 * Create a new DocumentViewer
32162 * @param {Object} config The config object
32165 Roo.bootstrap.DocumentViewer = function(config){
32166 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32171 * Fire after initEvent
32172 * @param {Roo.bootstrap.DocumentViewer} this
32178 * @param {Roo.bootstrap.DocumentViewer} this
32183 * Fire after download button
32184 * @param {Roo.bootstrap.DocumentViewer} this
32189 * Fire after trash button
32190 * @param {Roo.bootstrap.DocumentViewer} this
32197 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32199 showDownload : true,
32203 getAutoCreate : function()
32207 cls : 'roo-document-viewer',
32211 cls : 'roo-document-viewer-body',
32215 cls : 'roo-document-viewer-thumb',
32219 cls : 'roo-document-viewer-image'
32227 cls : 'roo-document-viewer-footer',
32230 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32234 cls : 'btn-group roo-document-viewer-download',
32238 cls : 'btn btn-default',
32239 html : '<i class="fa fa-download"></i>'
32245 cls : 'btn-group roo-document-viewer-trash',
32249 cls : 'btn btn-default',
32250 html : '<i class="fa fa-trash"></i>'
32263 initEvents : function()
32265 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32266 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32268 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32269 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32271 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32272 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32274 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32275 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32277 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32278 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32280 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32281 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32283 this.bodyEl.on('click', this.onClick, this);
32284 this.downloadBtn.on('click', this.onDownload, this);
32285 this.trashBtn.on('click', this.onTrash, this);
32287 this.downloadBtn.hide();
32288 this.trashBtn.hide();
32290 if(this.showDownload){
32291 this.downloadBtn.show();
32294 if(this.showTrash){
32295 this.trashBtn.show();
32298 if(!this.showDownload && !this.showTrash) {
32299 this.footerEl.hide();
32304 initial : function()
32306 this.fireEvent('initial', this);
32310 onClick : function(e)
32312 e.preventDefault();
32314 this.fireEvent('click', this);
32317 onDownload : function(e)
32319 e.preventDefault();
32321 this.fireEvent('download', this);
32324 onTrash : function(e)
32326 e.preventDefault();
32328 this.fireEvent('trash', this);
32340 * @class Roo.bootstrap.NavProgressBar
32341 * @extends Roo.bootstrap.Component
32342 * Bootstrap NavProgressBar class
32345 * Create a new nav progress bar
32346 * @param {Object} config The config object
32349 Roo.bootstrap.NavProgressBar = function(config){
32350 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32352 this.bullets = this.bullets || [];
32354 // Roo.bootstrap.NavProgressBar.register(this);
32358 * Fires when the active item changes
32359 * @param {Roo.bootstrap.NavProgressBar} this
32360 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32361 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32368 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32373 getAutoCreate : function()
32375 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32379 cls : 'roo-navigation-bar-group',
32383 cls : 'roo-navigation-top-bar'
32387 cls : 'roo-navigation-bullets-bar',
32391 cls : 'roo-navigation-bar'
32398 cls : 'roo-navigation-bottom-bar'
32408 initEvents: function()
32413 onRender : function(ct, position)
32415 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32417 if(this.bullets.length){
32418 Roo.each(this.bullets, function(b){
32427 addItem : function(cfg)
32429 var item = new Roo.bootstrap.NavProgressItem(cfg);
32431 item.parentId = this.id;
32432 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32435 var top = new Roo.bootstrap.Element({
32437 cls : 'roo-navigation-bar-text'
32440 var bottom = new Roo.bootstrap.Element({
32442 cls : 'roo-navigation-bar-text'
32445 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32446 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32448 var topText = new Roo.bootstrap.Element({
32450 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32453 var bottomText = new Roo.bootstrap.Element({
32455 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32458 topText.onRender(top.el, null);
32459 bottomText.onRender(bottom.el, null);
32462 item.bottomEl = bottom;
32465 this.barItems.push(item);
32470 getActive : function()
32472 var active = false;
32474 Roo.each(this.barItems, function(v){
32476 if (!v.isActive()) {
32488 setActiveItem : function(item)
32492 Roo.each(this.barItems, function(v){
32493 if (v.rid == item.rid) {
32497 if (v.isActive()) {
32498 v.setActive(false);
32503 item.setActive(true);
32505 this.fireEvent('changed', this, item, prev);
32508 getBarItem: function(rid)
32512 Roo.each(this.barItems, function(e) {
32513 if (e.rid != rid) {
32524 indexOfItem : function(item)
32528 Roo.each(this.barItems, function(v, i){
32530 if (v.rid != item.rid) {
32541 setActiveNext : function()
32543 var i = this.indexOfItem(this.getActive());
32545 if (i > this.barItems.length) {
32549 this.setActiveItem(this.barItems[i+1]);
32552 setActivePrev : function()
32554 var i = this.indexOfItem(this.getActive());
32560 this.setActiveItem(this.barItems[i-1]);
32563 format : function()
32565 if(!this.barItems.length){
32569 var width = 100 / this.barItems.length;
32571 Roo.each(this.barItems, function(i){
32572 i.el.setStyle('width', width + '%');
32573 i.topEl.el.setStyle('width', width + '%');
32574 i.bottomEl.el.setStyle('width', width + '%');
32583 * Nav Progress Item
32588 * @class Roo.bootstrap.NavProgressItem
32589 * @extends Roo.bootstrap.Component
32590 * Bootstrap NavProgressItem class
32591 * @cfg {String} rid the reference id
32592 * @cfg {Boolean} active (true|false) Is item active default false
32593 * @cfg {Boolean} disabled (true|false) Is item active default false
32594 * @cfg {String} html
32595 * @cfg {String} position (top|bottom) text position default bottom
32596 * @cfg {String} icon show icon instead of number
32599 * Create a new NavProgressItem
32600 * @param {Object} config The config object
32602 Roo.bootstrap.NavProgressItem = function(config){
32603 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32608 * The raw click event for the entire grid.
32609 * @param {Roo.bootstrap.NavProgressItem} this
32610 * @param {Roo.EventObject} e
32617 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32623 position : 'bottom',
32626 getAutoCreate : function()
32628 var iconCls = 'roo-navigation-bar-item-icon';
32630 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32634 cls: 'roo-navigation-bar-item',
32644 cfg.cls += ' active';
32647 cfg.cls += ' disabled';
32653 disable : function()
32655 this.setDisabled(true);
32658 enable : function()
32660 this.setDisabled(false);
32663 initEvents: function()
32665 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32667 this.iconEl.on('click', this.onClick, this);
32670 onClick : function(e)
32672 e.preventDefault();
32678 if(this.fireEvent('click', this, e) === false){
32682 this.parent().setActiveItem(this);
32685 isActive: function ()
32687 return this.active;
32690 setActive : function(state)
32692 if(this.active == state){
32696 this.active = state;
32699 this.el.addClass('active');
32703 this.el.removeClass('active');
32708 setDisabled : function(state)
32710 if(this.disabled == state){
32714 this.disabled = state;
32717 this.el.addClass('disabled');
32721 this.el.removeClass('disabled');
32724 tooltipEl : function()
32726 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32739 * @class Roo.bootstrap.FieldLabel
32740 * @extends Roo.bootstrap.Component
32741 * Bootstrap FieldLabel class
32742 * @cfg {String} html contents of the element
32743 * @cfg {String} tag tag of the element default label
32744 * @cfg {String} cls class of the element
32745 * @cfg {String} target label target
32746 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32747 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32748 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32749 * @cfg {String} iconTooltip default "This field is required"
32750 * @cfg {String} indicatorpos (left|right) default left
32753 * Create a new FieldLabel
32754 * @param {Object} config The config object
32757 Roo.bootstrap.FieldLabel = function(config){
32758 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32763 * Fires after the field has been marked as invalid.
32764 * @param {Roo.form.FieldLabel} this
32765 * @param {String} msg The validation message
32770 * Fires after the field has been validated with no errors.
32771 * @param {Roo.form.FieldLabel} this
32777 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32784 invalidClass : 'has-warning',
32785 validClass : 'has-success',
32786 iconTooltip : 'This field is required',
32787 indicatorpos : 'left',
32789 getAutoCreate : function(){
32792 if (!this.allowBlank) {
32798 cls : 'roo-bootstrap-field-label ' + this.cls,
32803 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32804 tooltip : this.iconTooltip
32813 if(this.indicatorpos == 'right'){
32816 cls : 'roo-bootstrap-field-label ' + this.cls,
32825 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32826 tooltip : this.iconTooltip
32835 initEvents: function()
32837 Roo.bootstrap.Element.superclass.initEvents.call(this);
32839 this.indicator = this.indicatorEl();
32841 if(this.indicator){
32842 this.indicator.removeClass('visible');
32843 this.indicator.addClass('invisible');
32846 Roo.bootstrap.FieldLabel.register(this);
32849 indicatorEl : function()
32851 var indicator = this.el.select('i.roo-required-indicator',true).first();
32862 * Mark this field as valid
32864 markValid : function()
32866 if(this.indicator){
32867 this.indicator.removeClass('visible');
32868 this.indicator.addClass('invisible');
32870 if (Roo.bootstrap.version == 3) {
32871 this.el.removeClass(this.invalidClass);
32872 this.el.addClass(this.validClass);
32874 this.el.removeClass('is-invalid');
32875 this.el.addClass('is-valid');
32879 this.fireEvent('valid', this);
32883 * Mark this field as invalid
32884 * @param {String} msg The validation message
32886 markInvalid : function(msg)
32888 if(this.indicator){
32889 this.indicator.removeClass('invisible');
32890 this.indicator.addClass('visible');
32892 if (Roo.bootstrap.version == 3) {
32893 this.el.removeClass(this.validClass);
32894 this.el.addClass(this.invalidClass);
32896 this.el.removeClass('is-valid');
32897 this.el.addClass('is-invalid');
32901 this.fireEvent('invalid', this, msg);
32907 Roo.apply(Roo.bootstrap.FieldLabel, {
32912 * register a FieldLabel Group
32913 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32915 register : function(label)
32917 if(this.groups.hasOwnProperty(label.target)){
32921 this.groups[label.target] = label;
32925 * fetch a FieldLabel Group based on the target
32926 * @param {string} target
32927 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32929 get: function(target) {
32930 if (typeof(this.groups[target]) == 'undefined') {
32934 return this.groups[target] ;
32943 * page DateSplitField.
32949 * @class Roo.bootstrap.DateSplitField
32950 * @extends Roo.bootstrap.Component
32951 * Bootstrap DateSplitField class
32952 * @cfg {string} fieldLabel - the label associated
32953 * @cfg {Number} labelWidth set the width of label (0-12)
32954 * @cfg {String} labelAlign (top|left)
32955 * @cfg {Boolean} dayAllowBlank (true|false) default false
32956 * @cfg {Boolean} monthAllowBlank (true|false) default false
32957 * @cfg {Boolean} yearAllowBlank (true|false) default false
32958 * @cfg {string} dayPlaceholder
32959 * @cfg {string} monthPlaceholder
32960 * @cfg {string} yearPlaceholder
32961 * @cfg {string} dayFormat default 'd'
32962 * @cfg {string} monthFormat default 'm'
32963 * @cfg {string} yearFormat default 'Y'
32964 * @cfg {Number} labellg set the width of label (1-12)
32965 * @cfg {Number} labelmd set the width of label (1-12)
32966 * @cfg {Number} labelsm set the width of label (1-12)
32967 * @cfg {Number} labelxs set the width of label (1-12)
32971 * Create a new DateSplitField
32972 * @param {Object} config The config object
32975 Roo.bootstrap.DateSplitField = function(config){
32976 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32982 * getting the data of years
32983 * @param {Roo.bootstrap.DateSplitField} this
32984 * @param {Object} years
32989 * getting the data of days
32990 * @param {Roo.bootstrap.DateSplitField} this
32991 * @param {Object} days
32996 * Fires after the field has been marked as invalid.
32997 * @param {Roo.form.Field} this
32998 * @param {String} msg The validation message
33003 * Fires after the field has been validated with no errors.
33004 * @param {Roo.form.Field} this
33010 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33013 labelAlign : 'top',
33015 dayAllowBlank : false,
33016 monthAllowBlank : false,
33017 yearAllowBlank : false,
33018 dayPlaceholder : '',
33019 monthPlaceholder : '',
33020 yearPlaceholder : '',
33024 isFormField : true,
33030 getAutoCreate : function()
33034 cls : 'row roo-date-split-field-group',
33039 cls : 'form-hidden-field roo-date-split-field-group-value',
33045 var labelCls = 'col-md-12';
33046 var contentCls = 'col-md-4';
33048 if(this.fieldLabel){
33052 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33056 html : this.fieldLabel
33061 if(this.labelAlign == 'left'){
33063 if(this.labelWidth > 12){
33064 label.style = "width: " + this.labelWidth + 'px';
33067 if(this.labelWidth < 13 && this.labelmd == 0){
33068 this.labelmd = this.labelWidth;
33071 if(this.labellg > 0){
33072 labelCls = ' col-lg-' + this.labellg;
33073 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33076 if(this.labelmd > 0){
33077 labelCls = ' col-md-' + this.labelmd;
33078 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33081 if(this.labelsm > 0){
33082 labelCls = ' col-sm-' + this.labelsm;
33083 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33086 if(this.labelxs > 0){
33087 labelCls = ' col-xs-' + this.labelxs;
33088 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33092 label.cls += ' ' + labelCls;
33094 cfg.cn.push(label);
33097 Roo.each(['day', 'month', 'year'], function(t){
33100 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33107 inputEl: function ()
33109 return this.el.select('.roo-date-split-field-group-value', true).first();
33112 onRender : function(ct, position)
33116 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33118 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33120 this.dayField = new Roo.bootstrap.ComboBox({
33121 allowBlank : this.dayAllowBlank,
33122 alwaysQuery : true,
33123 displayField : 'value',
33126 forceSelection : true,
33128 placeholder : this.dayPlaceholder,
33129 selectOnFocus : true,
33130 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33131 triggerAction : 'all',
33133 valueField : 'value',
33134 store : new Roo.data.SimpleStore({
33135 data : (function() {
33137 _this.fireEvent('days', _this, days);
33140 fields : [ 'value' ]
33143 select : function (_self, record, index)
33145 _this.setValue(_this.getValue());
33150 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33152 this.monthField = new Roo.bootstrap.MonthField({
33153 after : '<i class=\"fa fa-calendar\"></i>',
33154 allowBlank : this.monthAllowBlank,
33155 placeholder : this.monthPlaceholder,
33158 render : function (_self)
33160 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33161 e.preventDefault();
33165 select : function (_self, oldvalue, newvalue)
33167 _this.setValue(_this.getValue());
33172 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33174 this.yearField = new Roo.bootstrap.ComboBox({
33175 allowBlank : this.yearAllowBlank,
33176 alwaysQuery : true,
33177 displayField : 'value',
33180 forceSelection : true,
33182 placeholder : this.yearPlaceholder,
33183 selectOnFocus : true,
33184 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33185 triggerAction : 'all',
33187 valueField : 'value',
33188 store : new Roo.data.SimpleStore({
33189 data : (function() {
33191 _this.fireEvent('years', _this, years);
33194 fields : [ 'value' ]
33197 select : function (_self, record, index)
33199 _this.setValue(_this.getValue());
33204 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33207 setValue : function(v, format)
33209 this.inputEl.dom.value = v;
33211 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33213 var d = Date.parseDate(v, f);
33220 this.setDay(d.format(this.dayFormat));
33221 this.setMonth(d.format(this.monthFormat));
33222 this.setYear(d.format(this.yearFormat));
33229 setDay : function(v)
33231 this.dayField.setValue(v);
33232 this.inputEl.dom.value = this.getValue();
33237 setMonth : function(v)
33239 this.monthField.setValue(v, true);
33240 this.inputEl.dom.value = this.getValue();
33245 setYear : function(v)
33247 this.yearField.setValue(v);
33248 this.inputEl.dom.value = this.getValue();
33253 getDay : function()
33255 return this.dayField.getValue();
33258 getMonth : function()
33260 return this.monthField.getValue();
33263 getYear : function()
33265 return this.yearField.getValue();
33268 getValue : function()
33270 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33272 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33282 this.inputEl.dom.value = '';
33287 validate : function()
33289 var d = this.dayField.validate();
33290 var m = this.monthField.validate();
33291 var y = this.yearField.validate();
33296 (!this.dayAllowBlank && !d) ||
33297 (!this.monthAllowBlank && !m) ||
33298 (!this.yearAllowBlank && !y)
33303 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33312 this.markInvalid();
33317 markValid : function()
33320 var label = this.el.select('label', true).first();
33321 var icon = this.el.select('i.fa-star', true).first();
33327 this.fireEvent('valid', this);
33331 * Mark this field as invalid
33332 * @param {String} msg The validation message
33334 markInvalid : function(msg)
33337 var label = this.el.select('label', true).first();
33338 var icon = this.el.select('i.fa-star', true).first();
33340 if(label && !icon){
33341 this.el.select('.roo-date-split-field-label', true).createChild({
33343 cls : 'text-danger fa fa-lg fa-star',
33344 tooltip : 'This field is required',
33345 style : 'margin-right:5px;'
33349 this.fireEvent('invalid', this, msg);
33352 clearInvalid : function()
33354 var label = this.el.select('label', true).first();
33355 var icon = this.el.select('i.fa-star', true).first();
33361 this.fireEvent('valid', this);
33364 getName: function()
33374 * http://masonry.desandro.com
33376 * The idea is to render all the bricks based on vertical width...
33378 * The original code extends 'outlayer' - we might need to use that....
33384 * @class Roo.bootstrap.LayoutMasonry
33385 * @extends Roo.bootstrap.Component
33386 * Bootstrap Layout Masonry class
33389 * Create a new Element
33390 * @param {Object} config The config object
33393 Roo.bootstrap.LayoutMasonry = function(config){
33395 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33399 Roo.bootstrap.LayoutMasonry.register(this);
33405 * Fire after layout the items
33406 * @param {Roo.bootstrap.LayoutMasonry} this
33407 * @param {Roo.EventObject} e
33414 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33417 * @cfg {Boolean} isLayoutInstant = no animation?
33419 isLayoutInstant : false, // needed?
33422 * @cfg {Number} boxWidth width of the columns
33427 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33432 * @cfg {Number} padWidth padding below box..
33437 * @cfg {Number} gutter gutter width..
33442 * @cfg {Number} maxCols maximum number of columns
33448 * @cfg {Boolean} isAutoInitial defalut true
33450 isAutoInitial : true,
33455 * @cfg {Boolean} isHorizontal defalut false
33457 isHorizontal : false,
33459 currentSize : null,
33465 bricks: null, //CompositeElement
33469 _isLayoutInited : false,
33471 // isAlternative : false, // only use for vertical layout...
33474 * @cfg {Number} alternativePadWidth padding below box..
33476 alternativePadWidth : 50,
33478 selectedBrick : [],
33480 getAutoCreate : function(){
33482 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33486 cls: 'blog-masonary-wrapper ' + this.cls,
33488 cls : 'mas-boxes masonary'
33495 getChildContainer: function( )
33497 if (this.boxesEl) {
33498 return this.boxesEl;
33501 this.boxesEl = this.el.select('.mas-boxes').first();
33503 return this.boxesEl;
33507 initEvents : function()
33511 if(this.isAutoInitial){
33512 Roo.log('hook children rendered');
33513 this.on('childrenrendered', function() {
33514 Roo.log('children rendered');
33520 initial : function()
33522 this.selectedBrick = [];
33524 this.currentSize = this.el.getBox(true);
33526 Roo.EventManager.onWindowResize(this.resize, this);
33528 if(!this.isAutoInitial){
33536 //this.layout.defer(500,this);
33540 resize : function()
33542 var cs = this.el.getBox(true);
33545 this.currentSize.width == cs.width &&
33546 this.currentSize.x == cs.x &&
33547 this.currentSize.height == cs.height &&
33548 this.currentSize.y == cs.y
33550 Roo.log("no change in with or X or Y");
33554 this.currentSize = cs;
33560 layout : function()
33562 this._resetLayout();
33564 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33566 this.layoutItems( isInstant );
33568 this._isLayoutInited = true;
33570 this.fireEvent('layout', this);
33574 _resetLayout : function()
33576 if(this.isHorizontal){
33577 this.horizontalMeasureColumns();
33581 this.verticalMeasureColumns();
33585 verticalMeasureColumns : function()
33587 this.getContainerWidth();
33589 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33590 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33594 var boxWidth = this.boxWidth + this.padWidth;
33596 if(this.containerWidth < this.boxWidth){
33597 boxWidth = this.containerWidth
33600 var containerWidth = this.containerWidth;
33602 var cols = Math.floor(containerWidth / boxWidth);
33604 this.cols = Math.max( cols, 1 );
33606 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33608 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33610 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33612 this.colWidth = boxWidth + avail - this.padWidth;
33614 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33615 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33618 horizontalMeasureColumns : function()
33620 this.getContainerWidth();
33622 var boxWidth = this.boxWidth;
33624 if(this.containerWidth < boxWidth){
33625 boxWidth = this.containerWidth;
33628 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33630 this.el.setHeight(boxWidth);
33634 getContainerWidth : function()
33636 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33639 layoutItems : function( isInstant )
33641 Roo.log(this.bricks);
33643 var items = Roo.apply([], this.bricks);
33645 if(this.isHorizontal){
33646 this._horizontalLayoutItems( items , isInstant );
33650 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33651 // this._verticalAlternativeLayoutItems( items , isInstant );
33655 this._verticalLayoutItems( items , isInstant );
33659 _verticalLayoutItems : function ( items , isInstant)
33661 if ( !items || !items.length ) {
33666 ['xs', 'xs', 'xs', 'tall'],
33667 ['xs', 'xs', 'tall'],
33668 ['xs', 'xs', 'sm'],
33669 ['xs', 'xs', 'xs'],
33675 ['sm', 'xs', 'xs'],
33679 ['tall', 'xs', 'xs', 'xs'],
33680 ['tall', 'xs', 'xs'],
33692 Roo.each(items, function(item, k){
33694 switch (item.size) {
33695 // these layouts take up a full box,
33706 boxes.push([item]);
33729 var filterPattern = function(box, length)
33737 var pattern = box.slice(0, length);
33741 Roo.each(pattern, function(i){
33742 format.push(i.size);
33745 Roo.each(standard, function(s){
33747 if(String(s) != String(format)){
33756 if(!match && length == 1){
33761 filterPattern(box, length - 1);
33765 queue.push(pattern);
33767 box = box.slice(length, box.length);
33769 filterPattern(box, 4);
33775 Roo.each(boxes, function(box, k){
33781 if(box.length == 1){
33786 filterPattern(box, 4);
33790 this._processVerticalLayoutQueue( queue, isInstant );
33794 // _verticalAlternativeLayoutItems : function( items , isInstant )
33796 // if ( !items || !items.length ) {
33800 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33804 _horizontalLayoutItems : function ( items , isInstant)
33806 if ( !items || !items.length || items.length < 3) {
33812 var eItems = items.slice(0, 3);
33814 items = items.slice(3, items.length);
33817 ['xs', 'xs', 'xs', 'wide'],
33818 ['xs', 'xs', 'wide'],
33819 ['xs', 'xs', 'sm'],
33820 ['xs', 'xs', 'xs'],
33826 ['sm', 'xs', 'xs'],
33830 ['wide', 'xs', 'xs', 'xs'],
33831 ['wide', 'xs', 'xs'],
33844 Roo.each(items, function(item, k){
33846 switch (item.size) {
33857 boxes.push([item]);
33881 var filterPattern = function(box, length)
33889 var pattern = box.slice(0, length);
33893 Roo.each(pattern, function(i){
33894 format.push(i.size);
33897 Roo.each(standard, function(s){
33899 if(String(s) != String(format)){
33908 if(!match && length == 1){
33913 filterPattern(box, length - 1);
33917 queue.push(pattern);
33919 box = box.slice(length, box.length);
33921 filterPattern(box, 4);
33927 Roo.each(boxes, function(box, k){
33933 if(box.length == 1){
33938 filterPattern(box, 4);
33945 var pos = this.el.getBox(true);
33949 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33951 var hit_end = false;
33953 Roo.each(queue, function(box){
33957 Roo.each(box, function(b){
33959 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33969 Roo.each(box, function(b){
33971 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33974 mx = Math.max(mx, b.x);
33978 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33982 Roo.each(box, function(b){
33984 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33998 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34001 /** Sets position of item in DOM
34002 * @param {Element} item
34003 * @param {Number} x - horizontal position
34004 * @param {Number} y - vertical position
34005 * @param {Boolean} isInstant - disables transitions
34007 _processVerticalLayoutQueue : function( queue, isInstant )
34009 var pos = this.el.getBox(true);
34014 for (var i = 0; i < this.cols; i++){
34018 Roo.each(queue, function(box, k){
34020 var col = k % this.cols;
34022 Roo.each(box, function(b,kk){
34024 b.el.position('absolute');
34026 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34027 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34029 if(b.size == 'md-left' || b.size == 'md-right'){
34030 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34031 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34034 b.el.setWidth(width);
34035 b.el.setHeight(height);
34037 b.el.select('iframe',true).setSize(width,height);
34041 for (var i = 0; i < this.cols; i++){
34043 if(maxY[i] < maxY[col]){
34048 col = Math.min(col, i);
34052 x = pos.x + col * (this.colWidth + this.padWidth);
34056 var positions = [];
34058 switch (box.length){
34060 positions = this.getVerticalOneBoxColPositions(x, y, box);
34063 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34066 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34069 positions = this.getVerticalFourBoxColPositions(x, y, box);
34075 Roo.each(box, function(b,kk){
34077 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34079 var sz = b.el.getSize();
34081 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34089 for (var i = 0; i < this.cols; i++){
34090 mY = Math.max(mY, maxY[i]);
34093 this.el.setHeight(mY - pos.y);
34097 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34099 // var pos = this.el.getBox(true);
34102 // var maxX = pos.right;
34104 // var maxHeight = 0;
34106 // Roo.each(items, function(item, k){
34110 // item.el.position('absolute');
34112 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34114 // item.el.setWidth(width);
34116 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34118 // item.el.setHeight(height);
34121 // item.el.setXY([x, y], isInstant ? false : true);
34123 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34126 // y = y + height + this.alternativePadWidth;
34128 // maxHeight = maxHeight + height + this.alternativePadWidth;
34132 // this.el.setHeight(maxHeight);
34136 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34138 var pos = this.el.getBox(true);
34143 var maxX = pos.right;
34145 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34147 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34149 Roo.each(queue, function(box, k){
34151 Roo.each(box, function(b, kk){
34153 b.el.position('absolute');
34155 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34156 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34158 if(b.size == 'md-left' || b.size == 'md-right'){
34159 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34160 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34163 b.el.setWidth(width);
34164 b.el.setHeight(height);
34172 var positions = [];
34174 switch (box.length){
34176 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34179 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34182 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34185 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34191 Roo.each(box, function(b,kk){
34193 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34195 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34203 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34205 Roo.each(eItems, function(b,k){
34207 b.size = (k == 0) ? 'sm' : 'xs';
34208 b.x = (k == 0) ? 2 : 1;
34209 b.y = (k == 0) ? 2 : 1;
34211 b.el.position('absolute');
34213 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34215 b.el.setWidth(width);
34217 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34219 b.el.setHeight(height);
34223 var positions = [];
34226 x : maxX - this.unitWidth * 2 - this.gutter,
34231 x : maxX - this.unitWidth,
34232 y : minY + (this.unitWidth + this.gutter) * 2
34236 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34240 Roo.each(eItems, function(b,k){
34242 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34248 getVerticalOneBoxColPositions : function(x, y, box)
34252 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34254 if(box[0].size == 'md-left'){
34258 if(box[0].size == 'md-right'){
34263 x : x + (this.unitWidth + this.gutter) * rand,
34270 getVerticalTwoBoxColPositions : function(x, y, box)
34274 if(box[0].size == 'xs'){
34278 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34282 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34296 x : x + (this.unitWidth + this.gutter) * 2,
34297 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34304 getVerticalThreeBoxColPositions : function(x, y, box)
34308 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34316 x : x + (this.unitWidth + this.gutter) * 1,
34321 x : x + (this.unitWidth + this.gutter) * 2,
34329 if(box[0].size == 'xs' && box[1].size == 'xs'){
34338 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34342 x : x + (this.unitWidth + this.gutter) * 1,
34356 x : x + (this.unitWidth + this.gutter) * 2,
34361 x : x + (this.unitWidth + this.gutter) * 2,
34362 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34369 getVerticalFourBoxColPositions : function(x, y, box)
34373 if(box[0].size == 'xs'){
34382 y : y + (this.unitHeight + this.gutter) * 1
34387 y : y + (this.unitHeight + this.gutter) * 2
34391 x : x + (this.unitWidth + this.gutter) * 1,
34405 x : x + (this.unitWidth + this.gutter) * 2,
34410 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34411 y : y + (this.unitHeight + this.gutter) * 1
34415 x : x + (this.unitWidth + this.gutter) * 2,
34416 y : y + (this.unitWidth + this.gutter) * 2
34423 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34427 if(box[0].size == 'md-left'){
34429 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34436 if(box[0].size == 'md-right'){
34438 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34439 y : minY + (this.unitWidth + this.gutter) * 1
34445 var rand = Math.floor(Math.random() * (4 - box[0].y));
34448 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34449 y : minY + (this.unitWidth + this.gutter) * rand
34456 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34460 if(box[0].size == 'xs'){
34463 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34468 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34469 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34477 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34482 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34483 y : minY + (this.unitWidth + this.gutter) * 2
34490 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34494 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34497 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34502 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34503 y : minY + (this.unitWidth + this.gutter) * 1
34507 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34508 y : minY + (this.unitWidth + this.gutter) * 2
34515 if(box[0].size == 'xs' && box[1].size == 'xs'){
34518 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34523 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34528 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34529 y : minY + (this.unitWidth + this.gutter) * 1
34537 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34542 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34543 y : minY + (this.unitWidth + this.gutter) * 2
34547 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34548 y : minY + (this.unitWidth + this.gutter) * 2
34555 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34559 if(box[0].size == 'xs'){
34562 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34567 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34572 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),
34577 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34578 y : minY + (this.unitWidth + this.gutter) * 1
34586 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34591 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34592 y : minY + (this.unitWidth + this.gutter) * 2
34596 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34597 y : minY + (this.unitWidth + this.gutter) * 2
34601 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),
34602 y : minY + (this.unitWidth + this.gutter) * 2
34610 * remove a Masonry Brick
34611 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34613 removeBrick : function(brick_id)
34619 for (var i = 0; i<this.bricks.length; i++) {
34620 if (this.bricks[i].id == brick_id) {
34621 this.bricks.splice(i,1);
34622 this.el.dom.removeChild(Roo.get(brick_id).dom);
34629 * adds a Masonry Brick
34630 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34632 addBrick : function(cfg)
34634 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34635 //this.register(cn);
34636 cn.parentId = this.id;
34637 cn.render(this.el);
34642 * register a Masonry Brick
34643 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34646 register : function(brick)
34648 this.bricks.push(brick);
34649 brick.masonryId = this.id;
34653 * clear all the Masonry Brick
34655 clearAll : function()
34658 //this.getChildContainer().dom.innerHTML = "";
34659 this.el.dom.innerHTML = '';
34662 getSelected : function()
34664 if (!this.selectedBrick) {
34668 return this.selectedBrick;
34672 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34676 * register a Masonry Layout
34677 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34680 register : function(layout)
34682 this.groups[layout.id] = layout;
34685 * fetch a Masonry Layout based on the masonry layout ID
34686 * @param {string} the masonry layout to add
34687 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34690 get: function(layout_id) {
34691 if (typeof(this.groups[layout_id]) == 'undefined') {
34694 return this.groups[layout_id] ;
34706 * http://masonry.desandro.com
34708 * The idea is to render all the bricks based on vertical width...
34710 * The original code extends 'outlayer' - we might need to use that....
34716 * @class Roo.bootstrap.LayoutMasonryAuto
34717 * @extends Roo.bootstrap.Component
34718 * Bootstrap Layout Masonry class
34721 * Create a new Element
34722 * @param {Object} config The config object
34725 Roo.bootstrap.LayoutMasonryAuto = function(config){
34726 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34729 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34732 * @cfg {Boolean} isFitWidth - resize the width..
34734 isFitWidth : false, // options..
34736 * @cfg {Boolean} isOriginLeft = left align?
34738 isOriginLeft : true,
34740 * @cfg {Boolean} isOriginTop = top align?
34742 isOriginTop : false,
34744 * @cfg {Boolean} isLayoutInstant = no animation?
34746 isLayoutInstant : false, // needed?
34748 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34750 isResizingContainer : true,
34752 * @cfg {Number} columnWidth width of the columns
34758 * @cfg {Number} maxCols maximum number of columns
34763 * @cfg {Number} padHeight padding below box..
34769 * @cfg {Boolean} isAutoInitial defalut true
34772 isAutoInitial : true,
34778 initialColumnWidth : 0,
34779 currentSize : null,
34781 colYs : null, // array.
34788 bricks: null, //CompositeElement
34789 cols : 0, // array?
34790 // element : null, // wrapped now this.el
34791 _isLayoutInited : null,
34794 getAutoCreate : function(){
34798 cls: 'blog-masonary-wrapper ' + this.cls,
34800 cls : 'mas-boxes masonary'
34807 getChildContainer: function( )
34809 if (this.boxesEl) {
34810 return this.boxesEl;
34813 this.boxesEl = this.el.select('.mas-boxes').first();
34815 return this.boxesEl;
34819 initEvents : function()
34823 if(this.isAutoInitial){
34824 Roo.log('hook children rendered');
34825 this.on('childrenrendered', function() {
34826 Roo.log('children rendered');
34833 initial : function()
34835 this.reloadItems();
34837 this.currentSize = this.el.getBox(true);
34839 /// was window resize... - let's see if this works..
34840 Roo.EventManager.onWindowResize(this.resize, this);
34842 if(!this.isAutoInitial){
34847 this.layout.defer(500,this);
34850 reloadItems: function()
34852 this.bricks = this.el.select('.masonry-brick', true);
34854 this.bricks.each(function(b) {
34855 //Roo.log(b.getSize());
34856 if (!b.attr('originalwidth')) {
34857 b.attr('originalwidth', b.getSize().width);
34862 Roo.log(this.bricks.elements.length);
34865 resize : function()
34868 var cs = this.el.getBox(true);
34870 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34871 Roo.log("no change in with or X");
34874 this.currentSize = cs;
34878 layout : function()
34881 this._resetLayout();
34882 //this._manageStamps();
34884 // don't animate first layout
34885 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34886 this.layoutItems( isInstant );
34888 // flag for initalized
34889 this._isLayoutInited = true;
34892 layoutItems : function( isInstant )
34894 //var items = this._getItemsForLayout( this.items );
34895 // original code supports filtering layout items.. we just ignore it..
34897 this._layoutItems( this.bricks , isInstant );
34899 this._postLayout();
34901 _layoutItems : function ( items , isInstant)
34903 //this.fireEvent( 'layout', this, items );
34906 if ( !items || !items.elements.length ) {
34907 // no items, emit event with empty array
34912 items.each(function(item) {
34913 Roo.log("layout item");
34915 // get x/y object from method
34916 var position = this._getItemLayoutPosition( item );
34918 position.item = item;
34919 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34920 queue.push( position );
34923 this._processLayoutQueue( queue );
34925 /** Sets position of item in DOM
34926 * @param {Element} item
34927 * @param {Number} x - horizontal position
34928 * @param {Number} y - vertical position
34929 * @param {Boolean} isInstant - disables transitions
34931 _processLayoutQueue : function( queue )
34933 for ( var i=0, len = queue.length; i < len; i++ ) {
34934 var obj = queue[i];
34935 obj.item.position('absolute');
34936 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34942 * Any logic you want to do after each layout,
34943 * i.e. size the container
34945 _postLayout : function()
34947 this.resizeContainer();
34950 resizeContainer : function()
34952 if ( !this.isResizingContainer ) {
34955 var size = this._getContainerSize();
34957 this.el.setSize(size.width,size.height);
34958 this.boxesEl.setSize(size.width,size.height);
34964 _resetLayout : function()
34966 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34967 this.colWidth = this.el.getWidth();
34968 //this.gutter = this.el.getWidth();
34970 this.measureColumns();
34976 this.colYs.push( 0 );
34982 measureColumns : function()
34984 this.getContainerWidth();
34985 // if columnWidth is 0, default to outerWidth of first item
34986 if ( !this.columnWidth ) {
34987 var firstItem = this.bricks.first();
34988 Roo.log(firstItem);
34989 this.columnWidth = this.containerWidth;
34990 if (firstItem && firstItem.attr('originalwidth') ) {
34991 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34993 // columnWidth fall back to item of first element
34994 Roo.log("set column width?");
34995 this.initialColumnWidth = this.columnWidth ;
34997 // if first elem has no width, default to size of container
35002 if (this.initialColumnWidth) {
35003 this.columnWidth = this.initialColumnWidth;
35008 // column width is fixed at the top - however if container width get's smaller we should
35011 // this bit calcs how man columns..
35013 var columnWidth = this.columnWidth += this.gutter;
35015 // calculate columns
35016 var containerWidth = this.containerWidth + this.gutter;
35018 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35019 // fix rounding errors, typically with gutters
35020 var excess = columnWidth - containerWidth % columnWidth;
35023 // if overshoot is less than a pixel, round up, otherwise floor it
35024 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35025 cols = Math[ mathMethod ]( cols );
35026 this.cols = Math.max( cols, 1 );
35027 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35029 // padding positioning..
35030 var totalColWidth = this.cols * this.columnWidth;
35031 var padavail = this.containerWidth - totalColWidth;
35032 // so for 2 columns - we need 3 'pads'
35034 var padNeeded = (1+this.cols) * this.padWidth;
35036 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35038 this.columnWidth += padExtra
35039 //this.padWidth = Math.floor(padavail / ( this.cols));
35041 // adjust colum width so that padding is fixed??
35043 // we have 3 columns ... total = width * 3
35044 // we have X left over... that should be used by
35046 //if (this.expandC) {
35054 getContainerWidth : function()
35056 /* // container is parent if fit width
35057 var container = this.isFitWidth ? this.element.parentNode : this.element;
35058 // check that this.size and size are there
35059 // IE8 triggers resize on body size change, so they might not be
35061 var size = getSize( container ); //FIXME
35062 this.containerWidth = size && size.innerWidth; //FIXME
35065 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35069 _getItemLayoutPosition : function( item ) // what is item?
35071 // we resize the item to our columnWidth..
35073 item.setWidth(this.columnWidth);
35074 item.autoBoxAdjust = false;
35076 var sz = item.getSize();
35078 // how many columns does this brick span
35079 var remainder = this.containerWidth % this.columnWidth;
35081 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35082 // round if off by 1 pixel, otherwise use ceil
35083 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35084 colSpan = Math.min( colSpan, this.cols );
35086 // normally this should be '1' as we dont' currently allow multi width columns..
35088 var colGroup = this._getColGroup( colSpan );
35089 // get the minimum Y value from the columns
35090 var minimumY = Math.min.apply( Math, colGroup );
35091 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35093 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35095 // position the brick
35097 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35098 y: this.currentSize.y + minimumY + this.padHeight
35102 // apply setHeight to necessary columns
35103 var setHeight = minimumY + sz.height + this.padHeight;
35104 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35106 var setSpan = this.cols + 1 - colGroup.length;
35107 for ( var i = 0; i < setSpan; i++ ) {
35108 this.colYs[ shortColIndex + i ] = setHeight ;
35115 * @param {Number} colSpan - number of columns the element spans
35116 * @returns {Array} colGroup
35118 _getColGroup : function( colSpan )
35120 if ( colSpan < 2 ) {
35121 // if brick spans only one column, use all the column Ys
35126 // how many different places could this brick fit horizontally
35127 var groupCount = this.cols + 1 - colSpan;
35128 // for each group potential horizontal position
35129 for ( var i = 0; i < groupCount; i++ ) {
35130 // make an array of colY values for that one group
35131 var groupColYs = this.colYs.slice( i, i + colSpan );
35132 // and get the max value of the array
35133 colGroup[i] = Math.max.apply( Math, groupColYs );
35138 _manageStamp : function( stamp )
35140 var stampSize = stamp.getSize();
35141 var offset = stamp.getBox();
35142 // get the columns that this stamp affects
35143 var firstX = this.isOriginLeft ? offset.x : offset.right;
35144 var lastX = firstX + stampSize.width;
35145 var firstCol = Math.floor( firstX / this.columnWidth );
35146 firstCol = Math.max( 0, firstCol );
35148 var lastCol = Math.floor( lastX / this.columnWidth );
35149 // lastCol should not go over if multiple of columnWidth #425
35150 lastCol -= lastX % this.columnWidth ? 0 : 1;
35151 lastCol = Math.min( this.cols - 1, lastCol );
35153 // set colYs to bottom of the stamp
35154 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35157 for ( var i = firstCol; i <= lastCol; i++ ) {
35158 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35163 _getContainerSize : function()
35165 this.maxY = Math.max.apply( Math, this.colYs );
35170 if ( this.isFitWidth ) {
35171 size.width = this._getContainerFitWidth();
35177 _getContainerFitWidth : function()
35179 var unusedCols = 0;
35180 // count unused columns
35183 if ( this.colYs[i] !== 0 ) {
35188 // fit container to columns that have been used
35189 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35192 needsResizeLayout : function()
35194 var previousWidth = this.containerWidth;
35195 this.getContainerWidth();
35196 return previousWidth !== this.containerWidth;
35211 * @class Roo.bootstrap.MasonryBrick
35212 * @extends Roo.bootstrap.Component
35213 * Bootstrap MasonryBrick class
35216 * Create a new MasonryBrick
35217 * @param {Object} config The config object
35220 Roo.bootstrap.MasonryBrick = function(config){
35222 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35224 Roo.bootstrap.MasonryBrick.register(this);
35230 * When a MasonryBrick is clcik
35231 * @param {Roo.bootstrap.MasonryBrick} this
35232 * @param {Roo.EventObject} e
35238 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35241 * @cfg {String} title
35245 * @cfg {String} html
35249 * @cfg {String} bgimage
35253 * @cfg {String} videourl
35257 * @cfg {String} cls
35261 * @cfg {String} href
35265 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35270 * @cfg {String} placetitle (center|bottom)
35275 * @cfg {Boolean} isFitContainer defalut true
35277 isFitContainer : true,
35280 * @cfg {Boolean} preventDefault defalut false
35282 preventDefault : false,
35285 * @cfg {Boolean} inverse defalut false
35287 maskInverse : false,
35289 getAutoCreate : function()
35291 if(!this.isFitContainer){
35292 return this.getSplitAutoCreate();
35295 var cls = 'masonry-brick masonry-brick-full';
35297 if(this.href.length){
35298 cls += ' masonry-brick-link';
35301 if(this.bgimage.length){
35302 cls += ' masonry-brick-image';
35305 if(this.maskInverse){
35306 cls += ' mask-inverse';
35309 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35310 cls += ' enable-mask';
35314 cls += ' masonry-' + this.size + '-brick';
35317 if(this.placetitle.length){
35319 switch (this.placetitle) {
35321 cls += ' masonry-center-title';
35324 cls += ' masonry-bottom-title';
35331 if(!this.html.length && !this.bgimage.length){
35332 cls += ' masonry-center-title';
35335 if(!this.html.length && this.bgimage.length){
35336 cls += ' masonry-bottom-title';
35341 cls += ' ' + this.cls;
35345 tag: (this.href.length) ? 'a' : 'div',
35350 cls: 'masonry-brick-mask'
35354 cls: 'masonry-brick-paragraph',
35360 if(this.href.length){
35361 cfg.href = this.href;
35364 var cn = cfg.cn[1].cn;
35366 if(this.title.length){
35369 cls: 'masonry-brick-title',
35374 if(this.html.length){
35377 cls: 'masonry-brick-text',
35382 if (!this.title.length && !this.html.length) {
35383 cfg.cn[1].cls += ' hide';
35386 if(this.bgimage.length){
35389 cls: 'masonry-brick-image-view',
35394 if(this.videourl.length){
35395 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35396 // youtube support only?
35399 cls: 'masonry-brick-image-view',
35402 allowfullscreen : true
35410 getSplitAutoCreate : function()
35412 var cls = 'masonry-brick masonry-brick-split';
35414 if(this.href.length){
35415 cls += ' masonry-brick-link';
35418 if(this.bgimage.length){
35419 cls += ' masonry-brick-image';
35423 cls += ' masonry-' + this.size + '-brick';
35426 switch (this.placetitle) {
35428 cls += ' masonry-center-title';
35431 cls += ' masonry-bottom-title';
35434 if(!this.bgimage.length){
35435 cls += ' masonry-center-title';
35438 if(this.bgimage.length){
35439 cls += ' masonry-bottom-title';
35445 cls += ' ' + this.cls;
35449 tag: (this.href.length) ? 'a' : 'div',
35454 cls: 'masonry-brick-split-head',
35458 cls: 'masonry-brick-paragraph',
35465 cls: 'masonry-brick-split-body',
35471 if(this.href.length){
35472 cfg.href = this.href;
35475 if(this.title.length){
35476 cfg.cn[0].cn[0].cn.push({
35478 cls: 'masonry-brick-title',
35483 if(this.html.length){
35484 cfg.cn[1].cn.push({
35486 cls: 'masonry-brick-text',
35491 if(this.bgimage.length){
35492 cfg.cn[0].cn.push({
35494 cls: 'masonry-brick-image-view',
35499 if(this.videourl.length){
35500 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35501 // youtube support only?
35502 cfg.cn[0].cn.cn.push({
35504 cls: 'masonry-brick-image-view',
35507 allowfullscreen : true
35514 initEvents: function()
35516 switch (this.size) {
35549 this.el.on('touchstart', this.onTouchStart, this);
35550 this.el.on('touchmove', this.onTouchMove, this);
35551 this.el.on('touchend', this.onTouchEnd, this);
35552 this.el.on('contextmenu', this.onContextMenu, this);
35554 this.el.on('mouseenter' ,this.enter, this);
35555 this.el.on('mouseleave', this.leave, this);
35556 this.el.on('click', this.onClick, this);
35559 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35560 this.parent().bricks.push(this);
35565 onClick: function(e, el)
35567 var time = this.endTimer - this.startTimer;
35568 // Roo.log(e.preventDefault());
35571 e.preventDefault();
35576 if(!this.preventDefault){
35580 e.preventDefault();
35582 if (this.activeClass != '') {
35583 this.selectBrick();
35586 this.fireEvent('click', this, e);
35589 enter: function(e, el)
35591 e.preventDefault();
35593 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35597 if(this.bgimage.length && this.html.length){
35598 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35602 leave: function(e, el)
35604 e.preventDefault();
35606 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35610 if(this.bgimage.length && this.html.length){
35611 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35615 onTouchStart: function(e, el)
35617 // e.preventDefault();
35619 this.touchmoved = false;
35621 if(!this.isFitContainer){
35625 if(!this.bgimage.length || !this.html.length){
35629 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35631 this.timer = new Date().getTime();
35635 onTouchMove: function(e, el)
35637 this.touchmoved = true;
35640 onContextMenu : function(e,el)
35642 e.preventDefault();
35643 e.stopPropagation();
35647 onTouchEnd: function(e, el)
35649 // e.preventDefault();
35651 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35658 if(!this.bgimage.length || !this.html.length){
35660 if(this.href.length){
35661 window.location.href = this.href;
35667 if(!this.isFitContainer){
35671 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35673 window.location.href = this.href;
35676 //selection on single brick only
35677 selectBrick : function() {
35679 if (!this.parentId) {
35683 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35684 var index = m.selectedBrick.indexOf(this.id);
35687 m.selectedBrick.splice(index,1);
35688 this.el.removeClass(this.activeClass);
35692 for(var i = 0; i < m.selectedBrick.length; i++) {
35693 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35694 b.el.removeClass(b.activeClass);
35697 m.selectedBrick = [];
35699 m.selectedBrick.push(this.id);
35700 this.el.addClass(this.activeClass);
35704 isSelected : function(){
35705 return this.el.hasClass(this.activeClass);
35710 Roo.apply(Roo.bootstrap.MasonryBrick, {
35713 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35715 * register a Masonry Brick
35716 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35719 register : function(brick)
35721 //this.groups[brick.id] = brick;
35722 this.groups.add(brick.id, brick);
35725 * fetch a masonry brick based on the masonry brick ID
35726 * @param {string} the masonry brick to add
35727 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35730 get: function(brick_id)
35732 // if (typeof(this.groups[brick_id]) == 'undefined') {
35735 // return this.groups[brick_id] ;
35737 if(this.groups.key(brick_id)) {
35738 return this.groups.key(brick_id);
35756 * @class Roo.bootstrap.Brick
35757 * @extends Roo.bootstrap.Component
35758 * Bootstrap Brick class
35761 * Create a new Brick
35762 * @param {Object} config The config object
35765 Roo.bootstrap.Brick = function(config){
35766 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35772 * When a Brick is click
35773 * @param {Roo.bootstrap.Brick} this
35774 * @param {Roo.EventObject} e
35780 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35783 * @cfg {String} title
35787 * @cfg {String} html
35791 * @cfg {String} bgimage
35795 * @cfg {String} cls
35799 * @cfg {String} href
35803 * @cfg {String} video
35807 * @cfg {Boolean} square
35811 getAutoCreate : function()
35813 var cls = 'roo-brick';
35815 if(this.href.length){
35816 cls += ' roo-brick-link';
35819 if(this.bgimage.length){
35820 cls += ' roo-brick-image';
35823 if(!this.html.length && !this.bgimage.length){
35824 cls += ' roo-brick-center-title';
35827 if(!this.html.length && this.bgimage.length){
35828 cls += ' roo-brick-bottom-title';
35832 cls += ' ' + this.cls;
35836 tag: (this.href.length) ? 'a' : 'div',
35841 cls: 'roo-brick-paragraph',
35847 if(this.href.length){
35848 cfg.href = this.href;
35851 var cn = cfg.cn[0].cn;
35853 if(this.title.length){
35856 cls: 'roo-brick-title',
35861 if(this.html.length){
35864 cls: 'roo-brick-text',
35871 if(this.bgimage.length){
35874 cls: 'roo-brick-image-view',
35882 initEvents: function()
35884 if(this.title.length || this.html.length){
35885 this.el.on('mouseenter' ,this.enter, this);
35886 this.el.on('mouseleave', this.leave, this);
35889 Roo.EventManager.onWindowResize(this.resize, this);
35891 if(this.bgimage.length){
35892 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35893 this.imageEl.on('load', this.onImageLoad, this);
35900 onImageLoad : function()
35905 resize : function()
35907 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35909 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35911 if(this.bgimage.length){
35912 var image = this.el.select('.roo-brick-image-view', true).first();
35914 image.setWidth(paragraph.getWidth());
35917 image.setHeight(paragraph.getWidth());
35920 this.el.setHeight(image.getHeight());
35921 paragraph.setHeight(image.getHeight());
35927 enter: function(e, el)
35929 e.preventDefault();
35931 if(this.bgimage.length){
35932 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35933 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35937 leave: function(e, el)
35939 e.preventDefault();
35941 if(this.bgimage.length){
35942 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35943 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35958 * @class Roo.bootstrap.NumberField
35959 * @extends Roo.bootstrap.Input
35960 * Bootstrap NumberField class
35966 * Create a new NumberField
35967 * @param {Object} config The config object
35970 Roo.bootstrap.NumberField = function(config){
35971 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35974 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35977 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35979 allowDecimals : true,
35981 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35983 decimalSeparator : ".",
35985 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35987 decimalPrecision : 2,
35989 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35991 allowNegative : true,
35994 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35998 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36000 minValue : Number.NEGATIVE_INFINITY,
36002 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36004 maxValue : Number.MAX_VALUE,
36006 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36008 minText : "The minimum value for this field is {0}",
36010 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36012 maxText : "The maximum value for this field is {0}",
36014 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36015 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36017 nanText : "{0} is not a valid number",
36019 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36021 thousandsDelimiter : false,
36023 * @cfg {String} valueAlign alignment of value
36025 valueAlign : "left",
36027 getAutoCreate : function()
36029 var hiddenInput = {
36033 cls: 'hidden-number-input'
36037 hiddenInput.name = this.name;
36042 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36044 this.name = hiddenInput.name;
36046 if(cfg.cn.length > 0) {
36047 cfg.cn.push(hiddenInput);
36054 initEvents : function()
36056 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36058 var allowed = "0123456789";
36060 if(this.allowDecimals){
36061 allowed += this.decimalSeparator;
36064 if(this.allowNegative){
36068 if(this.thousandsDelimiter) {
36072 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36074 var keyPress = function(e){
36076 var k = e.getKey();
36078 var c = e.getCharCode();
36081 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36082 allowed.indexOf(String.fromCharCode(c)) === -1
36088 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36092 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36097 this.el.on("keypress", keyPress, this);
36100 validateValue : function(value)
36103 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36107 var num = this.parseValue(value);
36110 this.markInvalid(String.format(this.nanText, value));
36114 if(num < this.minValue){
36115 this.markInvalid(String.format(this.minText, this.minValue));
36119 if(num > this.maxValue){
36120 this.markInvalid(String.format(this.maxText, this.maxValue));
36127 getValue : function()
36129 var v = this.hiddenEl().getValue();
36131 return this.fixPrecision(this.parseValue(v));
36134 parseValue : function(value)
36136 if(this.thousandsDelimiter) {
36138 r = new RegExp(",", "g");
36139 value = value.replace(r, "");
36142 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36143 return isNaN(value) ? '' : value;
36146 fixPrecision : function(value)
36148 if(this.thousandsDelimiter) {
36150 r = new RegExp(",", "g");
36151 value = value.replace(r, "");
36154 var nan = isNaN(value);
36156 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36157 return nan ? '' : value;
36159 return parseFloat(value).toFixed(this.decimalPrecision);
36162 setValue : function(v)
36164 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36170 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36172 this.inputEl().dom.value = (v == '') ? '' :
36173 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36175 if(!this.allowZero && v === '0') {
36176 this.hiddenEl().dom.value = '';
36177 this.inputEl().dom.value = '';
36184 decimalPrecisionFcn : function(v)
36186 return Math.floor(v);
36189 beforeBlur : function()
36191 var v = this.parseValue(this.getRawValue());
36193 if(v || v === 0 || v === ''){
36198 hiddenEl : function()
36200 return this.el.select('input.hidden-number-input',true).first();
36212 * @class Roo.bootstrap.DocumentSlider
36213 * @extends Roo.bootstrap.Component
36214 * Bootstrap DocumentSlider class
36217 * Create a new DocumentViewer
36218 * @param {Object} config The config object
36221 Roo.bootstrap.DocumentSlider = function(config){
36222 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36229 * Fire after initEvent
36230 * @param {Roo.bootstrap.DocumentSlider} this
36235 * Fire after update
36236 * @param {Roo.bootstrap.DocumentSlider} this
36242 * @param {Roo.bootstrap.DocumentSlider} this
36248 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36254 getAutoCreate : function()
36258 cls : 'roo-document-slider',
36262 cls : 'roo-document-slider-header',
36266 cls : 'roo-document-slider-header-title'
36272 cls : 'roo-document-slider-body',
36276 cls : 'roo-document-slider-prev',
36280 cls : 'fa fa-chevron-left'
36286 cls : 'roo-document-slider-thumb',
36290 cls : 'roo-document-slider-image'
36296 cls : 'roo-document-slider-next',
36300 cls : 'fa fa-chevron-right'
36312 initEvents : function()
36314 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36315 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36317 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36318 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36320 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36321 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36323 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36324 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36326 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36327 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36329 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36330 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36332 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36333 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36335 this.thumbEl.on('click', this.onClick, this);
36337 this.prevIndicator.on('click', this.prev, this);
36339 this.nextIndicator.on('click', this.next, this);
36343 initial : function()
36345 if(this.files.length){
36346 this.indicator = 1;
36350 this.fireEvent('initial', this);
36353 update : function()
36355 this.imageEl.attr('src', this.files[this.indicator - 1]);
36357 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36359 this.prevIndicator.show();
36361 if(this.indicator == 1){
36362 this.prevIndicator.hide();
36365 this.nextIndicator.show();
36367 if(this.indicator == this.files.length){
36368 this.nextIndicator.hide();
36371 this.thumbEl.scrollTo('top');
36373 this.fireEvent('update', this);
36376 onClick : function(e)
36378 e.preventDefault();
36380 this.fireEvent('click', this);
36385 e.preventDefault();
36387 this.indicator = Math.max(1, this.indicator - 1);
36394 e.preventDefault();
36396 this.indicator = Math.min(this.files.length, this.indicator + 1);
36410 * @class Roo.bootstrap.RadioSet
36411 * @extends Roo.bootstrap.Input
36412 * Bootstrap RadioSet class
36413 * @cfg {String} indicatorpos (left|right) default left
36414 * @cfg {Boolean} inline (true|false) inline the element (default true)
36415 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36417 * Create a new RadioSet
36418 * @param {Object} config The config object
36421 Roo.bootstrap.RadioSet = function(config){
36423 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36427 Roo.bootstrap.RadioSet.register(this);
36432 * Fires when the element is checked or unchecked.
36433 * @param {Roo.bootstrap.RadioSet} this This radio
36434 * @param {Roo.bootstrap.Radio} item The checked item
36439 * Fires when the element is click.
36440 * @param {Roo.bootstrap.RadioSet} this This radio set
36441 * @param {Roo.bootstrap.Radio} item The checked item
36442 * @param {Roo.EventObject} e The event object
36449 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36457 indicatorpos : 'left',
36459 getAutoCreate : function()
36463 cls : 'roo-radio-set-label',
36467 html : this.fieldLabel
36471 if (Roo.bootstrap.version == 3) {
36474 if(this.indicatorpos == 'left'){
36477 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36478 tooltip : 'This field is required'
36483 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36484 tooltip : 'This field is required'
36490 cls : 'roo-radio-set-items'
36493 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36495 if (align === 'left' && this.fieldLabel.length) {
36498 cls : "roo-radio-set-right",
36504 if(this.labelWidth > 12){
36505 label.style = "width: " + this.labelWidth + 'px';
36508 if(this.labelWidth < 13 && this.labelmd == 0){
36509 this.labelmd = this.labelWidth;
36512 if(this.labellg > 0){
36513 label.cls += ' col-lg-' + this.labellg;
36514 items.cls += ' col-lg-' + (12 - this.labellg);
36517 if(this.labelmd > 0){
36518 label.cls += ' col-md-' + this.labelmd;
36519 items.cls += ' col-md-' + (12 - this.labelmd);
36522 if(this.labelsm > 0){
36523 label.cls += ' col-sm-' + this.labelsm;
36524 items.cls += ' col-sm-' + (12 - this.labelsm);
36527 if(this.labelxs > 0){
36528 label.cls += ' col-xs-' + this.labelxs;
36529 items.cls += ' col-xs-' + (12 - this.labelxs);
36535 cls : 'roo-radio-set',
36539 cls : 'roo-radio-set-input',
36542 value : this.value ? this.value : ''
36549 if(this.weight.length){
36550 cfg.cls += ' roo-radio-' + this.weight;
36554 cfg.cls += ' roo-radio-set-inline';
36558 ['xs','sm','md','lg'].map(function(size){
36559 if (settings[size]) {
36560 cfg.cls += ' col-' + size + '-' + settings[size];
36568 initEvents : function()
36570 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36571 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36573 if(!this.fieldLabel.length){
36574 this.labelEl.hide();
36577 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36578 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36580 this.indicator = this.indicatorEl();
36582 if(this.indicator){
36583 this.indicator.addClass('invisible');
36586 this.originalValue = this.getValue();
36590 inputEl: function ()
36592 return this.el.select('.roo-radio-set-input', true).first();
36595 getChildContainer : function()
36597 return this.itemsEl;
36600 register : function(item)
36602 this.radioes.push(item);
36606 validate : function()
36608 if(this.getVisibilityEl().hasClass('hidden')){
36614 Roo.each(this.radioes, function(i){
36623 if(this.allowBlank) {
36627 if(this.disabled || valid){
36632 this.markInvalid();
36637 markValid : function()
36639 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36640 this.indicatorEl().removeClass('visible');
36641 this.indicatorEl().addClass('invisible');
36645 if (Roo.bootstrap.version == 3) {
36646 this.el.removeClass([this.invalidClass, this.validClass]);
36647 this.el.addClass(this.validClass);
36649 this.el.removeClass(['is-invalid','is-valid']);
36650 this.el.addClass(['is-valid']);
36652 this.fireEvent('valid', this);
36655 markInvalid : function(msg)
36657 if(this.allowBlank || this.disabled){
36661 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36662 this.indicatorEl().removeClass('invisible');
36663 this.indicatorEl().addClass('visible');
36665 if (Roo.bootstrap.version == 3) {
36666 this.el.removeClass([this.invalidClass, this.validClass]);
36667 this.el.addClass(this.invalidClass);
36669 this.el.removeClass(['is-invalid','is-valid']);
36670 this.el.addClass(['is-invalid']);
36673 this.fireEvent('invalid', this, msg);
36677 setValue : function(v, suppressEvent)
36679 if(this.value === v){
36686 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36689 Roo.each(this.radioes, function(i){
36691 i.el.removeClass('checked');
36694 Roo.each(this.radioes, function(i){
36696 if(i.value === v || i.value.toString() === v.toString()){
36698 i.el.addClass('checked');
36700 if(suppressEvent !== true){
36701 this.fireEvent('check', this, i);
36712 clearInvalid : function(){
36714 if(!this.el || this.preventMark){
36718 this.el.removeClass([this.invalidClass]);
36720 this.fireEvent('valid', this);
36725 Roo.apply(Roo.bootstrap.RadioSet, {
36729 register : function(set)
36731 this.groups[set.name] = set;
36734 get: function(name)
36736 if (typeof(this.groups[name]) == 'undefined') {
36740 return this.groups[name] ;
36746 * Ext JS Library 1.1.1
36747 * Copyright(c) 2006-2007, Ext JS, LLC.
36749 * Originally Released Under LGPL - original licence link has changed is not relivant.
36752 * <script type="text/javascript">
36757 * @class Roo.bootstrap.SplitBar
36758 * @extends Roo.util.Observable
36759 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36763 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36764 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36765 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36766 split.minSize = 100;
36767 split.maxSize = 600;
36768 split.animate = true;
36769 split.on('moved', splitterMoved);
36772 * Create a new SplitBar
36773 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36774 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36775 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36776 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36777 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36778 position of the SplitBar).
36780 Roo.bootstrap.SplitBar = function(cfg){
36785 // dragElement : elm
36786 // resizingElement: el,
36788 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36789 // placement : Roo.bootstrap.SplitBar.LEFT ,
36790 // existingProxy ???
36793 this.el = Roo.get(cfg.dragElement, true);
36794 this.el.dom.unselectable = "on";
36796 this.resizingEl = Roo.get(cfg.resizingElement, true);
36800 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36801 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36804 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36807 * The minimum size of the resizing element. (Defaults to 0)
36813 * The maximum size of the resizing element. (Defaults to 2000)
36816 this.maxSize = 2000;
36819 * Whether to animate the transition to the new size
36822 this.animate = false;
36825 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36828 this.useShim = false;
36833 if(!cfg.existingProxy){
36835 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36837 this.proxy = Roo.get(cfg.existingProxy).dom;
36840 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36843 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36846 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36849 this.dragSpecs = {};
36852 * @private The adapter to use to positon and resize elements
36854 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36855 this.adapter.init(this);
36857 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36859 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36860 this.el.addClass("roo-splitbar-h");
36863 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36864 this.el.addClass("roo-splitbar-v");
36870 * Fires when the splitter is moved (alias for {@link #event-moved})
36871 * @param {Roo.bootstrap.SplitBar} this
36872 * @param {Number} newSize the new width or height
36877 * Fires when the splitter is moved
36878 * @param {Roo.bootstrap.SplitBar} this
36879 * @param {Number} newSize the new width or height
36883 * @event beforeresize
36884 * Fires before the splitter is dragged
36885 * @param {Roo.bootstrap.SplitBar} this
36887 "beforeresize" : true,
36889 "beforeapply" : true
36892 Roo.util.Observable.call(this);
36895 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36896 onStartProxyDrag : function(x, y){
36897 this.fireEvent("beforeresize", this);
36899 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36901 o.enableDisplayMode("block");
36902 // all splitbars share the same overlay
36903 Roo.bootstrap.SplitBar.prototype.overlay = o;
36905 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36906 this.overlay.show();
36907 Roo.get(this.proxy).setDisplayed("block");
36908 var size = this.adapter.getElementSize(this);
36909 this.activeMinSize = this.getMinimumSize();;
36910 this.activeMaxSize = this.getMaximumSize();;
36911 var c1 = size - this.activeMinSize;
36912 var c2 = Math.max(this.activeMaxSize - size, 0);
36913 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36914 this.dd.resetConstraints();
36915 this.dd.setXConstraint(
36916 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36917 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36919 this.dd.setYConstraint(0, 0);
36921 this.dd.resetConstraints();
36922 this.dd.setXConstraint(0, 0);
36923 this.dd.setYConstraint(
36924 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36925 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36928 this.dragSpecs.startSize = size;
36929 this.dragSpecs.startPoint = [x, y];
36930 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36934 * @private Called after the drag operation by the DDProxy
36936 onEndProxyDrag : function(e){
36937 Roo.get(this.proxy).setDisplayed(false);
36938 var endPoint = Roo.lib.Event.getXY(e);
36940 this.overlay.hide();
36943 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36944 newSize = this.dragSpecs.startSize +
36945 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36946 endPoint[0] - this.dragSpecs.startPoint[0] :
36947 this.dragSpecs.startPoint[0] - endPoint[0]
36950 newSize = this.dragSpecs.startSize +
36951 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36952 endPoint[1] - this.dragSpecs.startPoint[1] :
36953 this.dragSpecs.startPoint[1] - endPoint[1]
36956 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36957 if(newSize != this.dragSpecs.startSize){
36958 if(this.fireEvent('beforeapply', this, newSize) !== false){
36959 this.adapter.setElementSize(this, newSize);
36960 this.fireEvent("moved", this, newSize);
36961 this.fireEvent("resize", this, newSize);
36967 * Get the adapter this SplitBar uses
36968 * @return The adapter object
36970 getAdapter : function(){
36971 return this.adapter;
36975 * Set the adapter this SplitBar uses
36976 * @param {Object} adapter A SplitBar adapter object
36978 setAdapter : function(adapter){
36979 this.adapter = adapter;
36980 this.adapter.init(this);
36984 * Gets the minimum size for the resizing element
36985 * @return {Number} The minimum size
36987 getMinimumSize : function(){
36988 return this.minSize;
36992 * Sets the minimum size for the resizing element
36993 * @param {Number} minSize The minimum size
36995 setMinimumSize : function(minSize){
36996 this.minSize = minSize;
37000 * Gets the maximum size for the resizing element
37001 * @return {Number} The maximum size
37003 getMaximumSize : function(){
37004 return this.maxSize;
37008 * Sets the maximum size for the resizing element
37009 * @param {Number} maxSize The maximum size
37011 setMaximumSize : function(maxSize){
37012 this.maxSize = maxSize;
37016 * Sets the initialize size for the resizing element
37017 * @param {Number} size The initial size
37019 setCurrentSize : function(size){
37020 var oldAnimate = this.animate;
37021 this.animate = false;
37022 this.adapter.setElementSize(this, size);
37023 this.animate = oldAnimate;
37027 * Destroy this splitbar.
37028 * @param {Boolean} removeEl True to remove the element
37030 destroy : function(removeEl){
37032 this.shim.remove();
37035 this.proxy.parentNode.removeChild(this.proxy);
37043 * @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.
37045 Roo.bootstrap.SplitBar.createProxy = function(dir){
37046 var proxy = new Roo.Element(document.createElement("div"));
37047 proxy.unselectable();
37048 var cls = 'roo-splitbar-proxy';
37049 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37050 document.body.appendChild(proxy.dom);
37055 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37056 * Default Adapter. It assumes the splitter and resizing element are not positioned
37057 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37059 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37062 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37063 // do nothing for now
37064 init : function(s){
37068 * Called before drag operations to get the current size of the resizing element.
37069 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37071 getElementSize : function(s){
37072 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37073 return s.resizingEl.getWidth();
37075 return s.resizingEl.getHeight();
37080 * Called after drag operations to set the size of the resizing element.
37081 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37082 * @param {Number} newSize The new size to set
37083 * @param {Function} onComplete A function to be invoked when resizing is complete
37085 setElementSize : function(s, newSize, onComplete){
37086 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37088 s.resizingEl.setWidth(newSize);
37090 onComplete(s, newSize);
37093 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37098 s.resizingEl.setHeight(newSize);
37100 onComplete(s, newSize);
37103 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37110 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37111 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37112 * Adapter that moves the splitter element to align with the resized sizing element.
37113 * Used with an absolute positioned SplitBar.
37114 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37115 * document.body, make sure you assign an id to the body element.
37117 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37118 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37119 this.container = Roo.get(container);
37122 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37123 init : function(s){
37124 this.basic.init(s);
37127 getElementSize : function(s){
37128 return this.basic.getElementSize(s);
37131 setElementSize : function(s, newSize, onComplete){
37132 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37135 moveSplitter : function(s){
37136 var yes = Roo.bootstrap.SplitBar;
37137 switch(s.placement){
37139 s.el.setX(s.resizingEl.getRight());
37142 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37145 s.el.setY(s.resizingEl.getBottom());
37148 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37155 * Orientation constant - Create a vertical SplitBar
37159 Roo.bootstrap.SplitBar.VERTICAL = 1;
37162 * Orientation constant - Create a horizontal SplitBar
37166 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37169 * Placement constant - The resizing element is to the left of the splitter element
37173 Roo.bootstrap.SplitBar.LEFT = 1;
37176 * Placement constant - The resizing element is to the right of the splitter element
37180 Roo.bootstrap.SplitBar.RIGHT = 2;
37183 * Placement constant - The resizing element is positioned above the splitter element
37187 Roo.bootstrap.SplitBar.TOP = 3;
37190 * Placement constant - The resizing element is positioned under splitter element
37194 Roo.bootstrap.SplitBar.BOTTOM = 4;
37195 Roo.namespace("Roo.bootstrap.layout");/*
37197 * Ext JS Library 1.1.1
37198 * Copyright(c) 2006-2007, Ext JS, LLC.
37200 * Originally Released Under LGPL - original licence link has changed is not relivant.
37203 * <script type="text/javascript">
37207 * @class Roo.bootstrap.layout.Manager
37208 * @extends Roo.bootstrap.Component
37209 * Base class for layout managers.
37211 Roo.bootstrap.layout.Manager = function(config)
37213 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37219 /** false to disable window resize monitoring @type Boolean */
37220 this.monitorWindowResize = true;
37225 * Fires when a layout is performed.
37226 * @param {Roo.LayoutManager} this
37230 * @event regionresized
37231 * Fires when the user resizes a region.
37232 * @param {Roo.LayoutRegion} region The resized region
37233 * @param {Number} newSize The new size (width for east/west, height for north/south)
37235 "regionresized" : true,
37237 * @event regioncollapsed
37238 * Fires when a region is collapsed.
37239 * @param {Roo.LayoutRegion} region The collapsed region
37241 "regioncollapsed" : true,
37243 * @event regionexpanded
37244 * Fires when a region is expanded.
37245 * @param {Roo.LayoutRegion} region The expanded region
37247 "regionexpanded" : true
37249 this.updating = false;
37252 this.el = Roo.get(config.el);
37258 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37263 monitorWindowResize : true,
37269 onRender : function(ct, position)
37272 this.el = Roo.get(ct);
37275 //this.fireEvent('render',this);
37279 initEvents: function()
37283 // ie scrollbar fix
37284 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37285 document.body.scroll = "no";
37286 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37287 this.el.position('relative');
37289 this.id = this.el.id;
37290 this.el.addClass("roo-layout-container");
37291 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37292 if(this.el.dom != document.body ) {
37293 this.el.on('resize', this.layout,this);
37294 this.el.on('show', this.layout,this);
37300 * Returns true if this layout is currently being updated
37301 * @return {Boolean}
37303 isUpdating : function(){
37304 return this.updating;
37308 * Suspend the LayoutManager from doing auto-layouts while
37309 * making multiple add or remove calls
37311 beginUpdate : function(){
37312 this.updating = true;
37316 * Restore auto-layouts and optionally disable the manager from performing a layout
37317 * @param {Boolean} noLayout true to disable a layout update
37319 endUpdate : function(noLayout){
37320 this.updating = false;
37326 layout: function(){
37330 onRegionResized : function(region, newSize){
37331 this.fireEvent("regionresized", region, newSize);
37335 onRegionCollapsed : function(region){
37336 this.fireEvent("regioncollapsed", region);
37339 onRegionExpanded : function(region){
37340 this.fireEvent("regionexpanded", region);
37344 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37345 * performs box-model adjustments.
37346 * @return {Object} The size as an object {width: (the width), height: (the height)}
37348 getViewSize : function()
37351 if(this.el.dom != document.body){
37352 size = this.el.getSize();
37354 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37356 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37357 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37362 * Returns the Element this layout is bound to.
37363 * @return {Roo.Element}
37365 getEl : function(){
37370 * Returns the specified region.
37371 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37372 * @return {Roo.LayoutRegion}
37374 getRegion : function(target){
37375 return this.regions[target.toLowerCase()];
37378 onWindowResize : function(){
37379 if(this.monitorWindowResize){
37386 * Ext JS Library 1.1.1
37387 * Copyright(c) 2006-2007, Ext JS, LLC.
37389 * Originally Released Under LGPL - original licence link has changed is not relivant.
37392 * <script type="text/javascript">
37395 * @class Roo.bootstrap.layout.Border
37396 * @extends Roo.bootstrap.layout.Manager
37397 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37398 * please see: examples/bootstrap/nested.html<br><br>
37400 <b>The container the layout is rendered into can be either the body element or any other element.
37401 If it is not the body element, the container needs to either be an absolute positioned element,
37402 or you will need to add "position:relative" to the css of the container. You will also need to specify
37403 the container size if it is not the body element.</b>
37406 * Create a new Border
37407 * @param {Object} config Configuration options
37409 Roo.bootstrap.layout.Border = function(config){
37410 config = config || {};
37411 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37415 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37416 if(config[region]){
37417 config[region].region = region;
37418 this.addRegion(config[region]);
37424 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37426 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37428 parent : false, // this might point to a 'nest' or a ???
37431 * Creates and adds a new region if it doesn't already exist.
37432 * @param {String} target The target region key (north, south, east, west or center).
37433 * @param {Object} config The regions config object
37434 * @return {BorderLayoutRegion} The new region
37436 addRegion : function(config)
37438 if(!this.regions[config.region]){
37439 var r = this.factory(config);
37440 this.bindRegion(r);
37442 return this.regions[config.region];
37446 bindRegion : function(r){
37447 this.regions[r.config.region] = r;
37449 r.on("visibilitychange", this.layout, this);
37450 r.on("paneladded", this.layout, this);
37451 r.on("panelremoved", this.layout, this);
37452 r.on("invalidated", this.layout, this);
37453 r.on("resized", this.onRegionResized, this);
37454 r.on("collapsed", this.onRegionCollapsed, this);
37455 r.on("expanded", this.onRegionExpanded, this);
37459 * Performs a layout update.
37461 layout : function()
37463 if(this.updating) {
37467 // render all the rebions if they have not been done alreayd?
37468 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37469 if(this.regions[region] && !this.regions[region].bodyEl){
37470 this.regions[region].onRender(this.el)
37474 var size = this.getViewSize();
37475 var w = size.width;
37476 var h = size.height;
37481 //var x = 0, y = 0;
37483 var rs = this.regions;
37484 var north = rs["north"];
37485 var south = rs["south"];
37486 var west = rs["west"];
37487 var east = rs["east"];
37488 var center = rs["center"];
37489 //if(this.hideOnLayout){ // not supported anymore
37490 //c.el.setStyle("display", "none");
37492 if(north && north.isVisible()){
37493 var b = north.getBox();
37494 var m = north.getMargins();
37495 b.width = w - (m.left+m.right);
37498 centerY = b.height + b.y + m.bottom;
37499 centerH -= centerY;
37500 north.updateBox(this.safeBox(b));
37502 if(south && south.isVisible()){
37503 var b = south.getBox();
37504 var m = south.getMargins();
37505 b.width = w - (m.left+m.right);
37507 var totalHeight = (b.height + m.top + m.bottom);
37508 b.y = h - totalHeight + m.top;
37509 centerH -= totalHeight;
37510 south.updateBox(this.safeBox(b));
37512 if(west && west.isVisible()){
37513 var b = west.getBox();
37514 var m = west.getMargins();
37515 b.height = centerH - (m.top+m.bottom);
37517 b.y = centerY + m.top;
37518 var totalWidth = (b.width + m.left + m.right);
37519 centerX += totalWidth;
37520 centerW -= totalWidth;
37521 west.updateBox(this.safeBox(b));
37523 if(east && east.isVisible()){
37524 var b = east.getBox();
37525 var m = east.getMargins();
37526 b.height = centerH - (m.top+m.bottom);
37527 var totalWidth = (b.width + m.left + m.right);
37528 b.x = w - totalWidth + m.left;
37529 b.y = centerY + m.top;
37530 centerW -= totalWidth;
37531 east.updateBox(this.safeBox(b));
37534 var m = center.getMargins();
37536 x: centerX + m.left,
37537 y: centerY + m.top,
37538 width: centerW - (m.left+m.right),
37539 height: centerH - (m.top+m.bottom)
37541 //if(this.hideOnLayout){
37542 //center.el.setStyle("display", "block");
37544 center.updateBox(this.safeBox(centerBox));
37547 this.fireEvent("layout", this);
37551 safeBox : function(box){
37552 box.width = Math.max(0, box.width);
37553 box.height = Math.max(0, box.height);
37558 * Adds a ContentPanel (or subclass) to this layout.
37559 * @param {String} target The target region key (north, south, east, west or center).
37560 * @param {Roo.ContentPanel} panel The panel to add
37561 * @return {Roo.ContentPanel} The added panel
37563 add : function(target, panel){
37565 target = target.toLowerCase();
37566 return this.regions[target].add(panel);
37570 * Remove a ContentPanel (or subclass) to this layout.
37571 * @param {String} target The target region key (north, south, east, west or center).
37572 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37573 * @return {Roo.ContentPanel} The removed panel
37575 remove : function(target, panel){
37576 target = target.toLowerCase();
37577 return this.regions[target].remove(panel);
37581 * Searches all regions for a panel with the specified id
37582 * @param {String} panelId
37583 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37585 findPanel : function(panelId){
37586 var rs = this.regions;
37587 for(var target in rs){
37588 if(typeof rs[target] != "function"){
37589 var p = rs[target].getPanel(panelId);
37599 * Searches all regions for a panel with the specified id and activates (shows) it.
37600 * @param {String/ContentPanel} panelId The panels id or the panel itself
37601 * @return {Roo.ContentPanel} The shown panel or null
37603 showPanel : function(panelId) {
37604 var rs = this.regions;
37605 for(var target in rs){
37606 var r = rs[target];
37607 if(typeof r != "function"){
37608 if(r.hasPanel(panelId)){
37609 return r.showPanel(panelId);
37617 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37618 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37621 restoreState : function(provider){
37623 provider = Roo.state.Manager;
37625 var sm = new Roo.LayoutStateManager();
37626 sm.init(this, provider);
37632 * Adds a xtype elements to the layout.
37636 xtype : 'ContentPanel',
37643 xtype : 'NestedLayoutPanel',
37649 items : [ ... list of content panels or nested layout panels.. ]
37653 * @param {Object} cfg Xtype definition of item to add.
37655 addxtype : function(cfg)
37657 // basically accepts a pannel...
37658 // can accept a layout region..!?!?
37659 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37662 // theory? children can only be panels??
37664 //if (!cfg.xtype.match(/Panel$/)) {
37669 if (typeof(cfg.region) == 'undefined') {
37670 Roo.log("Failed to add Panel, region was not set");
37674 var region = cfg.region;
37680 xitems = cfg.items;
37685 if ( region == 'center') {
37686 Roo.log("Center: " + cfg.title);
37692 case 'Content': // ContentPanel (el, cfg)
37693 case 'Scroll': // ContentPanel (el, cfg)
37695 cfg.autoCreate = cfg.autoCreate || true;
37696 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37698 // var el = this.el.createChild();
37699 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37702 this.add(region, ret);
37706 case 'TreePanel': // our new panel!
37707 cfg.el = this.el.createChild();
37708 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37709 this.add(region, ret);
37714 // create a new Layout (which is a Border Layout...
37716 var clayout = cfg.layout;
37717 clayout.el = this.el.createChild();
37718 clayout.items = clayout.items || [];
37722 // replace this exitems with the clayout ones..
37723 xitems = clayout.items;
37725 // force background off if it's in center...
37726 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37727 cfg.background = false;
37729 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37732 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37733 //console.log('adding nested layout panel ' + cfg.toSource());
37734 this.add(region, ret);
37735 nb = {}; /// find first...
37740 // needs grid and region
37742 //var el = this.getRegion(region).el.createChild();
37744 *var el = this.el.createChild();
37745 // create the grid first...
37746 cfg.grid.container = el;
37747 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37750 if (region == 'center' && this.active ) {
37751 cfg.background = false;
37754 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37756 this.add(region, ret);
37758 if (cfg.background) {
37759 // render grid on panel activation (if panel background)
37760 ret.on('activate', function(gp) {
37761 if (!gp.grid.rendered) {
37762 // gp.grid.render(el);
37766 // cfg.grid.render(el);
37772 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37773 // it was the old xcomponent building that caused this before.
37774 // espeically if border is the top element in the tree.
37784 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37786 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37787 this.add(region, ret);
37791 throw "Can not add '" + cfg.xtype + "' to Border";
37797 this.beginUpdate();
37801 Roo.each(xitems, function(i) {
37802 region = nb && i.region ? i.region : false;
37804 var add = ret.addxtype(i);
37807 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37808 if (!i.background) {
37809 abn[region] = nb[region] ;
37816 // make the last non-background panel active..
37817 //if (nb) { Roo.log(abn); }
37820 for(var r in abn) {
37821 region = this.getRegion(r);
37823 // tried using nb[r], but it does not work..
37825 region.showPanel(abn[r]);
37836 factory : function(cfg)
37839 var validRegions = Roo.bootstrap.layout.Border.regions;
37841 var target = cfg.region;
37844 var r = Roo.bootstrap.layout;
37848 return new r.North(cfg);
37850 return new r.South(cfg);
37852 return new r.East(cfg);
37854 return new r.West(cfg);
37856 return new r.Center(cfg);
37858 throw 'Layout region "'+target+'" not supported.';
37865 * Ext JS Library 1.1.1
37866 * Copyright(c) 2006-2007, Ext JS, LLC.
37868 * Originally Released Under LGPL - original licence link has changed is not relivant.
37871 * <script type="text/javascript">
37875 * @class Roo.bootstrap.layout.Basic
37876 * @extends Roo.util.Observable
37877 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37878 * and does not have a titlebar, tabs or any other features. All it does is size and position
37879 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37880 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37881 * @cfg {string} region the region that it inhabits..
37882 * @cfg {bool} skipConfig skip config?
37886 Roo.bootstrap.layout.Basic = function(config){
37888 this.mgr = config.mgr;
37890 this.position = config.region;
37892 var skipConfig = config.skipConfig;
37896 * @scope Roo.BasicLayoutRegion
37900 * @event beforeremove
37901 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37902 * @param {Roo.LayoutRegion} this
37903 * @param {Roo.ContentPanel} panel The panel
37904 * @param {Object} e The cancel event object
37906 "beforeremove" : true,
37908 * @event invalidated
37909 * Fires when the layout for this region is changed.
37910 * @param {Roo.LayoutRegion} this
37912 "invalidated" : true,
37914 * @event visibilitychange
37915 * Fires when this region is shown or hidden
37916 * @param {Roo.LayoutRegion} this
37917 * @param {Boolean} visibility true or false
37919 "visibilitychange" : true,
37921 * @event paneladded
37922 * Fires when a panel is added.
37923 * @param {Roo.LayoutRegion} this
37924 * @param {Roo.ContentPanel} panel The panel
37926 "paneladded" : true,
37928 * @event panelremoved
37929 * Fires when a panel is removed.
37930 * @param {Roo.LayoutRegion} this
37931 * @param {Roo.ContentPanel} panel The panel
37933 "panelremoved" : true,
37935 * @event beforecollapse
37936 * Fires when this region before collapse.
37937 * @param {Roo.LayoutRegion} this
37939 "beforecollapse" : true,
37942 * Fires when this region is collapsed.
37943 * @param {Roo.LayoutRegion} this
37945 "collapsed" : true,
37948 * Fires when this region is expanded.
37949 * @param {Roo.LayoutRegion} this
37954 * Fires when this region is slid into view.
37955 * @param {Roo.LayoutRegion} this
37957 "slideshow" : true,
37960 * Fires when this region slides out of view.
37961 * @param {Roo.LayoutRegion} this
37963 "slidehide" : true,
37965 * @event panelactivated
37966 * Fires when a panel is activated.
37967 * @param {Roo.LayoutRegion} this
37968 * @param {Roo.ContentPanel} panel The activated panel
37970 "panelactivated" : true,
37973 * Fires when the user resizes this region.
37974 * @param {Roo.LayoutRegion} this
37975 * @param {Number} newSize The new size (width for east/west, height for north/south)
37979 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37980 this.panels = new Roo.util.MixedCollection();
37981 this.panels.getKey = this.getPanelId.createDelegate(this);
37983 this.activePanel = null;
37984 // ensure listeners are added...
37986 if (config.listeners || config.events) {
37987 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37988 listeners : config.listeners || {},
37989 events : config.events || {}
37993 if(skipConfig !== true){
37994 this.applyConfig(config);
37998 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38000 getPanelId : function(p){
38004 applyConfig : function(config){
38005 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38006 this.config = config;
38011 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38012 * the width, for horizontal (north, south) the height.
38013 * @param {Number} newSize The new width or height
38015 resizeTo : function(newSize){
38016 var el = this.el ? this.el :
38017 (this.activePanel ? this.activePanel.getEl() : null);
38019 switch(this.position){
38022 el.setWidth(newSize);
38023 this.fireEvent("resized", this, newSize);
38027 el.setHeight(newSize);
38028 this.fireEvent("resized", this, newSize);
38034 getBox : function(){
38035 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38038 getMargins : function(){
38039 return this.margins;
38042 updateBox : function(box){
38044 var el = this.activePanel.getEl();
38045 el.dom.style.left = box.x + "px";
38046 el.dom.style.top = box.y + "px";
38047 this.activePanel.setSize(box.width, box.height);
38051 * Returns the container element for this region.
38052 * @return {Roo.Element}
38054 getEl : function(){
38055 return this.activePanel;
38059 * Returns true if this region is currently visible.
38060 * @return {Boolean}
38062 isVisible : function(){
38063 return this.activePanel ? true : false;
38066 setActivePanel : function(panel){
38067 panel = this.getPanel(panel);
38068 if(this.activePanel && this.activePanel != panel){
38069 this.activePanel.setActiveState(false);
38070 this.activePanel.getEl().setLeftTop(-10000,-10000);
38072 this.activePanel = panel;
38073 panel.setActiveState(true);
38075 panel.setSize(this.box.width, this.box.height);
38077 this.fireEvent("panelactivated", this, panel);
38078 this.fireEvent("invalidated");
38082 * Show the specified panel.
38083 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38084 * @return {Roo.ContentPanel} The shown panel or null
38086 showPanel : function(panel){
38087 panel = this.getPanel(panel);
38089 this.setActivePanel(panel);
38095 * Get the active panel for this region.
38096 * @return {Roo.ContentPanel} The active panel or null
38098 getActivePanel : function(){
38099 return this.activePanel;
38103 * Add the passed ContentPanel(s)
38104 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38105 * @return {Roo.ContentPanel} The panel added (if only one was added)
38107 add : function(panel){
38108 if(arguments.length > 1){
38109 for(var i = 0, len = arguments.length; i < len; i++) {
38110 this.add(arguments[i]);
38114 if(this.hasPanel(panel)){
38115 this.showPanel(panel);
38118 var el = panel.getEl();
38119 if(el.dom.parentNode != this.mgr.el.dom){
38120 this.mgr.el.dom.appendChild(el.dom);
38122 if(panel.setRegion){
38123 panel.setRegion(this);
38125 this.panels.add(panel);
38126 el.setStyle("position", "absolute");
38127 if(!panel.background){
38128 this.setActivePanel(panel);
38129 if(this.config.initialSize && this.panels.getCount()==1){
38130 this.resizeTo(this.config.initialSize);
38133 this.fireEvent("paneladded", this, panel);
38138 * Returns true if the panel is in this region.
38139 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38140 * @return {Boolean}
38142 hasPanel : function(panel){
38143 if(typeof panel == "object"){ // must be panel obj
38144 panel = panel.getId();
38146 return this.getPanel(panel) ? true : false;
38150 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38151 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38152 * @param {Boolean} preservePanel Overrides the config preservePanel option
38153 * @return {Roo.ContentPanel} The panel that was removed
38155 remove : function(panel, preservePanel){
38156 panel = this.getPanel(panel);
38161 this.fireEvent("beforeremove", this, panel, e);
38162 if(e.cancel === true){
38165 var panelId = panel.getId();
38166 this.panels.removeKey(panelId);
38171 * Returns the panel specified or null if it's not in this region.
38172 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38173 * @return {Roo.ContentPanel}
38175 getPanel : function(id){
38176 if(typeof id == "object"){ // must be panel obj
38179 return this.panels.get(id);
38183 * Returns this regions position (north/south/east/west/center).
38186 getPosition: function(){
38187 return this.position;
38191 * Ext JS Library 1.1.1
38192 * Copyright(c) 2006-2007, Ext JS, LLC.
38194 * Originally Released Under LGPL - original licence link has changed is not relivant.
38197 * <script type="text/javascript">
38201 * @class Roo.bootstrap.layout.Region
38202 * @extends Roo.bootstrap.layout.Basic
38203 * This class represents a region in a layout manager.
38205 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38206 * @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})
38207 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38208 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38209 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38210 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38211 * @cfg {String} title The title for the region (overrides panel titles)
38212 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38213 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38214 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38215 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38216 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38217 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38218 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38219 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38220 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38221 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38223 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38224 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38225 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38226 * @cfg {Number} width For East/West panels
38227 * @cfg {Number} height For North/South panels
38228 * @cfg {Boolean} split To show the splitter
38229 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38231 * @cfg {string} cls Extra CSS classes to add to region
38233 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38234 * @cfg {string} region the region that it inhabits..
38237 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38238 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38240 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38241 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38242 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38244 Roo.bootstrap.layout.Region = function(config)
38246 this.applyConfig(config);
38248 var mgr = config.mgr;
38249 var pos = config.region;
38250 config.skipConfig = true;
38251 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38254 this.onRender(mgr.el);
38257 this.visible = true;
38258 this.collapsed = false;
38259 this.unrendered_panels = [];
38262 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38264 position: '', // set by wrapper (eg. north/south etc..)
38265 unrendered_panels : null, // unrendered panels.
38267 tabPosition : false,
38269 mgr: false, // points to 'Border'
38272 createBody : function(){
38273 /** This region's body element
38274 * @type Roo.Element */
38275 this.bodyEl = this.el.createChild({
38277 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38281 onRender: function(ctr, pos)
38283 var dh = Roo.DomHelper;
38284 /** This region's container element
38285 * @type Roo.Element */
38286 this.el = dh.append(ctr.dom, {
38288 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38290 /** This region's title element
38291 * @type Roo.Element */
38293 this.titleEl = dh.append(this.el.dom, {
38295 unselectable: "on",
38296 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38298 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38299 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38303 this.titleEl.enableDisplayMode();
38304 /** This region's title text element
38305 * @type HTMLElement */
38306 this.titleTextEl = this.titleEl.dom.firstChild;
38307 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38309 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38310 this.closeBtn.enableDisplayMode();
38311 this.closeBtn.on("click", this.closeClicked, this);
38312 this.closeBtn.hide();
38314 this.createBody(this.config);
38315 if(this.config.hideWhenEmpty){
38317 this.on("paneladded", this.validateVisibility, this);
38318 this.on("panelremoved", this.validateVisibility, this);
38320 if(this.autoScroll){
38321 this.bodyEl.setStyle("overflow", "auto");
38323 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38325 //if(c.titlebar !== false){
38326 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38327 this.titleEl.hide();
38329 this.titleEl.show();
38330 if(this.config.title){
38331 this.titleTextEl.innerHTML = this.config.title;
38335 if(this.config.collapsed){
38336 this.collapse(true);
38338 if(this.config.hidden){
38342 if (this.unrendered_panels && this.unrendered_panels.length) {
38343 for (var i =0;i< this.unrendered_panels.length; i++) {
38344 this.add(this.unrendered_panels[i]);
38346 this.unrendered_panels = null;
38352 applyConfig : function(c)
38355 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38356 var dh = Roo.DomHelper;
38357 if(c.titlebar !== false){
38358 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38359 this.collapseBtn.on("click", this.collapse, this);
38360 this.collapseBtn.enableDisplayMode();
38362 if(c.showPin === true || this.showPin){
38363 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38364 this.stickBtn.enableDisplayMode();
38365 this.stickBtn.on("click", this.expand, this);
38366 this.stickBtn.hide();
38371 /** This region's collapsed element
38372 * @type Roo.Element */
38375 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38376 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38379 if(c.floatable !== false){
38380 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38381 this.collapsedEl.on("click", this.collapseClick, this);
38384 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38385 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38386 id: "message", unselectable: "on", style:{"float":"left"}});
38387 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38389 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38390 this.expandBtn.on("click", this.expand, this);
38394 if(this.collapseBtn){
38395 this.collapseBtn.setVisible(c.collapsible == true);
38398 this.cmargins = c.cmargins || this.cmargins ||
38399 (this.position == "west" || this.position == "east" ?
38400 {top: 0, left: 2, right:2, bottom: 0} :
38401 {top: 2, left: 0, right:0, bottom: 2});
38403 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38406 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38408 this.autoScroll = c.autoScroll || false;
38413 this.duration = c.duration || .30;
38414 this.slideDuration = c.slideDuration || .45;
38419 * Returns true if this region is currently visible.
38420 * @return {Boolean}
38422 isVisible : function(){
38423 return this.visible;
38427 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38428 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38430 //setCollapsedTitle : function(title){
38431 // title = title || " ";
38432 // if(this.collapsedTitleTextEl){
38433 // this.collapsedTitleTextEl.innerHTML = title;
38437 getBox : function(){
38439 // if(!this.collapsed){
38440 b = this.el.getBox(false, true);
38442 // b = this.collapsedEl.getBox(false, true);
38447 getMargins : function(){
38448 return this.margins;
38449 //return this.collapsed ? this.cmargins : this.margins;
38452 highlight : function(){
38453 this.el.addClass("x-layout-panel-dragover");
38456 unhighlight : function(){
38457 this.el.removeClass("x-layout-panel-dragover");
38460 updateBox : function(box)
38462 if (!this.bodyEl) {
38463 return; // not rendered yet..
38467 if(!this.collapsed){
38468 this.el.dom.style.left = box.x + "px";
38469 this.el.dom.style.top = box.y + "px";
38470 this.updateBody(box.width, box.height);
38472 this.collapsedEl.dom.style.left = box.x + "px";
38473 this.collapsedEl.dom.style.top = box.y + "px";
38474 this.collapsedEl.setSize(box.width, box.height);
38477 this.tabs.autoSizeTabs();
38481 updateBody : function(w, h)
38484 this.el.setWidth(w);
38485 w -= this.el.getBorderWidth("rl");
38486 if(this.config.adjustments){
38487 w += this.config.adjustments[0];
38490 if(h !== null && h > 0){
38491 this.el.setHeight(h);
38492 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38493 h -= this.el.getBorderWidth("tb");
38494 if(this.config.adjustments){
38495 h += this.config.adjustments[1];
38497 this.bodyEl.setHeight(h);
38499 h = this.tabs.syncHeight(h);
38502 if(this.panelSize){
38503 w = w !== null ? w : this.panelSize.width;
38504 h = h !== null ? h : this.panelSize.height;
38506 if(this.activePanel){
38507 var el = this.activePanel.getEl();
38508 w = w !== null ? w : el.getWidth();
38509 h = h !== null ? h : el.getHeight();
38510 this.panelSize = {width: w, height: h};
38511 this.activePanel.setSize(w, h);
38513 if(Roo.isIE && this.tabs){
38514 this.tabs.el.repaint();
38519 * Returns the container element for this region.
38520 * @return {Roo.Element}
38522 getEl : function(){
38527 * Hides this region.
38530 //if(!this.collapsed){
38531 this.el.dom.style.left = "-2000px";
38534 // this.collapsedEl.dom.style.left = "-2000px";
38535 // this.collapsedEl.hide();
38537 this.visible = false;
38538 this.fireEvent("visibilitychange", this, false);
38542 * Shows this region if it was previously hidden.
38545 //if(!this.collapsed){
38548 // this.collapsedEl.show();
38550 this.visible = true;
38551 this.fireEvent("visibilitychange", this, true);
38554 closeClicked : function(){
38555 if(this.activePanel){
38556 this.remove(this.activePanel);
38560 collapseClick : function(e){
38562 e.stopPropagation();
38565 e.stopPropagation();
38571 * Collapses this region.
38572 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38575 collapse : function(skipAnim, skipCheck = false){
38576 if(this.collapsed) {
38580 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38582 this.collapsed = true;
38584 this.split.el.hide();
38586 if(this.config.animate && skipAnim !== true){
38587 this.fireEvent("invalidated", this);
38588 this.animateCollapse();
38590 this.el.setLocation(-20000,-20000);
38592 this.collapsedEl.show();
38593 this.fireEvent("collapsed", this);
38594 this.fireEvent("invalidated", this);
38600 animateCollapse : function(){
38605 * Expands this region if it was previously collapsed.
38606 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38607 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38610 expand : function(e, skipAnim){
38612 e.stopPropagation();
38614 if(!this.collapsed || this.el.hasActiveFx()) {
38618 this.afterSlideIn();
38621 this.collapsed = false;
38622 if(this.config.animate && skipAnim !== true){
38623 this.animateExpand();
38627 this.split.el.show();
38629 this.collapsedEl.setLocation(-2000,-2000);
38630 this.collapsedEl.hide();
38631 this.fireEvent("invalidated", this);
38632 this.fireEvent("expanded", this);
38636 animateExpand : function(){
38640 initTabs : function()
38642 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38644 var ts = new Roo.bootstrap.panel.Tabs({
38645 el: this.bodyEl.dom,
38647 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38648 disableTooltips: this.config.disableTabTips,
38649 toolbar : this.config.toolbar
38652 if(this.config.hideTabs){
38653 ts.stripWrap.setDisplayed(false);
38656 ts.resizeTabs = this.config.resizeTabs === true;
38657 ts.minTabWidth = this.config.minTabWidth || 40;
38658 ts.maxTabWidth = this.config.maxTabWidth || 250;
38659 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38660 ts.monitorResize = false;
38661 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38662 ts.bodyEl.addClass('roo-layout-tabs-body');
38663 this.panels.each(this.initPanelAsTab, this);
38666 initPanelAsTab : function(panel){
38667 var ti = this.tabs.addTab(
38671 this.config.closeOnTab && panel.isClosable(),
38674 if(panel.tabTip !== undefined){
38675 ti.setTooltip(panel.tabTip);
38677 ti.on("activate", function(){
38678 this.setActivePanel(panel);
38681 if(this.config.closeOnTab){
38682 ti.on("beforeclose", function(t, e){
38684 this.remove(panel);
38688 panel.tabItem = ti;
38693 updatePanelTitle : function(panel, title)
38695 if(this.activePanel == panel){
38696 this.updateTitle(title);
38699 var ti = this.tabs.getTab(panel.getEl().id);
38701 if(panel.tabTip !== undefined){
38702 ti.setTooltip(panel.tabTip);
38707 updateTitle : function(title){
38708 if(this.titleTextEl && !this.config.title){
38709 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38713 setActivePanel : function(panel)
38715 panel = this.getPanel(panel);
38716 if(this.activePanel && this.activePanel != panel){
38717 if(this.activePanel.setActiveState(false) === false){
38721 this.activePanel = panel;
38722 panel.setActiveState(true);
38723 if(this.panelSize){
38724 panel.setSize(this.panelSize.width, this.panelSize.height);
38727 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38729 this.updateTitle(panel.getTitle());
38731 this.fireEvent("invalidated", this);
38733 this.fireEvent("panelactivated", this, panel);
38737 * Shows the specified panel.
38738 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38739 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38741 showPanel : function(panel)
38743 panel = this.getPanel(panel);
38746 var tab = this.tabs.getTab(panel.getEl().id);
38747 if(tab.isHidden()){
38748 this.tabs.unhideTab(tab.id);
38752 this.setActivePanel(panel);
38759 * Get the active panel for this region.
38760 * @return {Roo.ContentPanel} The active panel or null
38762 getActivePanel : function(){
38763 return this.activePanel;
38766 validateVisibility : function(){
38767 if(this.panels.getCount() < 1){
38768 this.updateTitle(" ");
38769 this.closeBtn.hide();
38772 if(!this.isVisible()){
38779 * Adds the passed ContentPanel(s) to this region.
38780 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38781 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38783 add : function(panel)
38785 if(arguments.length > 1){
38786 for(var i = 0, len = arguments.length; i < len; i++) {
38787 this.add(arguments[i]);
38792 // if we have not been rendered yet, then we can not really do much of this..
38793 if (!this.bodyEl) {
38794 this.unrendered_panels.push(panel);
38801 if(this.hasPanel(panel)){
38802 this.showPanel(panel);
38805 panel.setRegion(this);
38806 this.panels.add(panel);
38807 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38808 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38809 // and hide them... ???
38810 this.bodyEl.dom.appendChild(panel.getEl().dom);
38811 if(panel.background !== true){
38812 this.setActivePanel(panel);
38814 this.fireEvent("paneladded", this, panel);
38821 this.initPanelAsTab(panel);
38825 if(panel.background !== true){
38826 this.tabs.activate(panel.getEl().id);
38828 this.fireEvent("paneladded", this, panel);
38833 * Hides the tab for the specified panel.
38834 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38836 hidePanel : function(panel){
38837 if(this.tabs && (panel = this.getPanel(panel))){
38838 this.tabs.hideTab(panel.getEl().id);
38843 * Unhides the tab for a previously hidden panel.
38844 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38846 unhidePanel : function(panel){
38847 if(this.tabs && (panel = this.getPanel(panel))){
38848 this.tabs.unhideTab(panel.getEl().id);
38852 clearPanels : function(){
38853 while(this.panels.getCount() > 0){
38854 this.remove(this.panels.first());
38859 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38860 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38861 * @param {Boolean} preservePanel Overrides the config preservePanel option
38862 * @return {Roo.ContentPanel} The panel that was removed
38864 remove : function(panel, preservePanel)
38866 panel = this.getPanel(panel);
38871 this.fireEvent("beforeremove", this, panel, e);
38872 if(e.cancel === true){
38875 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38876 var panelId = panel.getId();
38877 this.panels.removeKey(panelId);
38879 document.body.appendChild(panel.getEl().dom);
38882 this.tabs.removeTab(panel.getEl().id);
38883 }else if (!preservePanel){
38884 this.bodyEl.dom.removeChild(panel.getEl().dom);
38886 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38887 var p = this.panels.first();
38888 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38889 tempEl.appendChild(p.getEl().dom);
38890 this.bodyEl.update("");
38891 this.bodyEl.dom.appendChild(p.getEl().dom);
38893 this.updateTitle(p.getTitle());
38895 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38896 this.setActivePanel(p);
38898 panel.setRegion(null);
38899 if(this.activePanel == panel){
38900 this.activePanel = null;
38902 if(this.config.autoDestroy !== false && preservePanel !== true){
38903 try{panel.destroy();}catch(e){}
38905 this.fireEvent("panelremoved", this, panel);
38910 * Returns the TabPanel component used by this region
38911 * @return {Roo.TabPanel}
38913 getTabs : function(){
38917 createTool : function(parentEl, className){
38918 var btn = Roo.DomHelper.append(parentEl, {
38920 cls: "x-layout-tools-button",
38923 cls: "roo-layout-tools-button-inner " + className,
38927 btn.addClassOnOver("roo-layout-tools-button-over");
38932 * Ext JS Library 1.1.1
38933 * Copyright(c) 2006-2007, Ext JS, LLC.
38935 * Originally Released Under LGPL - original licence link has changed is not relivant.
38938 * <script type="text/javascript">
38944 * @class Roo.SplitLayoutRegion
38945 * @extends Roo.LayoutRegion
38946 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38948 Roo.bootstrap.layout.Split = function(config){
38949 this.cursor = config.cursor;
38950 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38953 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38955 splitTip : "Drag to resize.",
38956 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38957 useSplitTips : false,
38959 applyConfig : function(config){
38960 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38963 onRender : function(ctr,pos) {
38965 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38966 if(!this.config.split){
38971 var splitEl = Roo.DomHelper.append(ctr.dom, {
38973 id: this.el.id + "-split",
38974 cls: "roo-layout-split roo-layout-split-"+this.position,
38977 /** The SplitBar for this region
38978 * @type Roo.SplitBar */
38979 // does not exist yet...
38980 Roo.log([this.position, this.orientation]);
38982 this.split = new Roo.bootstrap.SplitBar({
38983 dragElement : splitEl,
38984 resizingElement: this.el,
38985 orientation : this.orientation
38988 this.split.on("moved", this.onSplitMove, this);
38989 this.split.useShim = this.config.useShim === true;
38990 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38991 if(this.useSplitTips){
38992 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38994 //if(config.collapsible){
38995 // this.split.el.on("dblclick", this.collapse, this);
38998 if(typeof this.config.minSize != "undefined"){
38999 this.split.minSize = this.config.minSize;
39001 if(typeof this.config.maxSize != "undefined"){
39002 this.split.maxSize = this.config.maxSize;
39004 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39005 this.hideSplitter();
39010 getHMaxSize : function(){
39011 var cmax = this.config.maxSize || 10000;
39012 var center = this.mgr.getRegion("center");
39013 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39016 getVMaxSize : function(){
39017 var cmax = this.config.maxSize || 10000;
39018 var center = this.mgr.getRegion("center");
39019 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39022 onSplitMove : function(split, newSize){
39023 this.fireEvent("resized", this, newSize);
39027 * Returns the {@link Roo.SplitBar} for this region.
39028 * @return {Roo.SplitBar}
39030 getSplitBar : function(){
39035 this.hideSplitter();
39036 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39039 hideSplitter : function(){
39041 this.split.el.setLocation(-2000,-2000);
39042 this.split.el.hide();
39048 this.split.el.show();
39050 Roo.bootstrap.layout.Split.superclass.show.call(this);
39053 beforeSlide: function(){
39054 if(Roo.isGecko){// firefox overflow auto bug workaround
39055 this.bodyEl.clip();
39057 this.tabs.bodyEl.clip();
39059 if(this.activePanel){
39060 this.activePanel.getEl().clip();
39062 if(this.activePanel.beforeSlide){
39063 this.activePanel.beforeSlide();
39069 afterSlide : function(){
39070 if(Roo.isGecko){// firefox overflow auto bug workaround
39071 this.bodyEl.unclip();
39073 this.tabs.bodyEl.unclip();
39075 if(this.activePanel){
39076 this.activePanel.getEl().unclip();
39077 if(this.activePanel.afterSlide){
39078 this.activePanel.afterSlide();
39084 initAutoHide : function(){
39085 if(this.autoHide !== false){
39086 if(!this.autoHideHd){
39087 var st = new Roo.util.DelayedTask(this.slideIn, this);
39088 this.autoHideHd = {
39089 "mouseout": function(e){
39090 if(!e.within(this.el, true)){
39094 "mouseover" : function(e){
39100 this.el.on(this.autoHideHd);
39104 clearAutoHide : function(){
39105 if(this.autoHide !== false){
39106 this.el.un("mouseout", this.autoHideHd.mouseout);
39107 this.el.un("mouseover", this.autoHideHd.mouseover);
39111 clearMonitor : function(){
39112 Roo.get(document).un("click", this.slideInIf, this);
39115 // these names are backwards but not changed for compat
39116 slideOut : function(){
39117 if(this.isSlid || this.el.hasActiveFx()){
39120 this.isSlid = true;
39121 if(this.collapseBtn){
39122 this.collapseBtn.hide();
39124 this.closeBtnState = this.closeBtn.getStyle('display');
39125 this.closeBtn.hide();
39127 this.stickBtn.show();
39130 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39131 this.beforeSlide();
39132 this.el.setStyle("z-index", 10001);
39133 this.el.slideIn(this.getSlideAnchor(), {
39134 callback: function(){
39136 this.initAutoHide();
39137 Roo.get(document).on("click", this.slideInIf, this);
39138 this.fireEvent("slideshow", this);
39145 afterSlideIn : function(){
39146 this.clearAutoHide();
39147 this.isSlid = false;
39148 this.clearMonitor();
39149 this.el.setStyle("z-index", "");
39150 if(this.collapseBtn){
39151 this.collapseBtn.show();
39153 this.closeBtn.setStyle('display', this.closeBtnState);
39155 this.stickBtn.hide();
39157 this.fireEvent("slidehide", this);
39160 slideIn : function(cb){
39161 if(!this.isSlid || this.el.hasActiveFx()){
39165 this.isSlid = false;
39166 this.beforeSlide();
39167 this.el.slideOut(this.getSlideAnchor(), {
39168 callback: function(){
39169 this.el.setLeftTop(-10000, -10000);
39171 this.afterSlideIn();
39179 slideInIf : function(e){
39180 if(!e.within(this.el)){
39185 animateCollapse : function(){
39186 this.beforeSlide();
39187 this.el.setStyle("z-index", 20000);
39188 var anchor = this.getSlideAnchor();
39189 this.el.slideOut(anchor, {
39190 callback : function(){
39191 this.el.setStyle("z-index", "");
39192 this.collapsedEl.slideIn(anchor, {duration:.3});
39194 this.el.setLocation(-10000,-10000);
39196 this.fireEvent("collapsed", this);
39203 animateExpand : function(){
39204 this.beforeSlide();
39205 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39206 this.el.setStyle("z-index", 20000);
39207 this.collapsedEl.hide({
39210 this.el.slideIn(this.getSlideAnchor(), {
39211 callback : function(){
39212 this.el.setStyle("z-index", "");
39215 this.split.el.show();
39217 this.fireEvent("invalidated", this);
39218 this.fireEvent("expanded", this);
39246 getAnchor : function(){
39247 return this.anchors[this.position];
39250 getCollapseAnchor : function(){
39251 return this.canchors[this.position];
39254 getSlideAnchor : function(){
39255 return this.sanchors[this.position];
39258 getAlignAdj : function(){
39259 var cm = this.cmargins;
39260 switch(this.position){
39276 getExpandAdj : function(){
39277 var c = this.collapsedEl, cm = this.cmargins;
39278 switch(this.position){
39280 return [-(cm.right+c.getWidth()+cm.left), 0];
39283 return [cm.right+c.getWidth()+cm.left, 0];
39286 return [0, -(cm.top+cm.bottom+c.getHeight())];
39289 return [0, cm.top+cm.bottom+c.getHeight()];
39295 * Ext JS Library 1.1.1
39296 * Copyright(c) 2006-2007, Ext JS, LLC.
39298 * Originally Released Under LGPL - original licence link has changed is not relivant.
39301 * <script type="text/javascript">
39304 * These classes are private internal classes
39306 Roo.bootstrap.layout.Center = function(config){
39307 config.region = "center";
39308 Roo.bootstrap.layout.Region.call(this, config);
39309 this.visible = true;
39310 this.minWidth = config.minWidth || 20;
39311 this.minHeight = config.minHeight || 20;
39314 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39316 // center panel can't be hidden
39320 // center panel can't be hidden
39323 getMinWidth: function(){
39324 return this.minWidth;
39327 getMinHeight: function(){
39328 return this.minHeight;
39342 Roo.bootstrap.layout.North = function(config)
39344 config.region = 'north';
39345 config.cursor = 'n-resize';
39347 Roo.bootstrap.layout.Split.call(this, config);
39351 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39352 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39353 this.split.el.addClass("roo-layout-split-v");
39355 //var size = config.initialSize || config.height;
39356 //if(this.el && typeof size != "undefined"){
39357 // this.el.setHeight(size);
39360 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39362 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39365 onRender : function(ctr, pos)
39367 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39368 var size = this.config.initialSize || this.config.height;
39369 if(this.el && typeof size != "undefined"){
39370 this.el.setHeight(size);
39375 getBox : function(){
39376 if(this.collapsed){
39377 return this.collapsedEl.getBox();
39379 var box = this.el.getBox();
39381 box.height += this.split.el.getHeight();
39386 updateBox : function(box){
39387 if(this.split && !this.collapsed){
39388 box.height -= this.split.el.getHeight();
39389 this.split.el.setLeft(box.x);
39390 this.split.el.setTop(box.y+box.height);
39391 this.split.el.setWidth(box.width);
39393 if(this.collapsed){
39394 this.updateBody(box.width, null);
39396 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39404 Roo.bootstrap.layout.South = function(config){
39405 config.region = 'south';
39406 config.cursor = 's-resize';
39407 Roo.bootstrap.layout.Split.call(this, config);
39409 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39410 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39411 this.split.el.addClass("roo-layout-split-v");
39416 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39417 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39419 onRender : function(ctr, pos)
39421 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39422 var size = this.config.initialSize || this.config.height;
39423 if(this.el && typeof size != "undefined"){
39424 this.el.setHeight(size);
39429 getBox : function(){
39430 if(this.collapsed){
39431 return this.collapsedEl.getBox();
39433 var box = this.el.getBox();
39435 var sh = this.split.el.getHeight();
39442 updateBox : function(box){
39443 if(this.split && !this.collapsed){
39444 var sh = this.split.el.getHeight();
39447 this.split.el.setLeft(box.x);
39448 this.split.el.setTop(box.y-sh);
39449 this.split.el.setWidth(box.width);
39451 if(this.collapsed){
39452 this.updateBody(box.width, null);
39454 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39458 Roo.bootstrap.layout.East = function(config){
39459 config.region = "east";
39460 config.cursor = "e-resize";
39461 Roo.bootstrap.layout.Split.call(this, config);
39463 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39464 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39465 this.split.el.addClass("roo-layout-split-h");
39469 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39470 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39472 onRender : function(ctr, pos)
39474 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39475 var size = this.config.initialSize || this.config.width;
39476 if(this.el && typeof size != "undefined"){
39477 this.el.setWidth(size);
39482 getBox : function(){
39483 if(this.collapsed){
39484 return this.collapsedEl.getBox();
39486 var box = this.el.getBox();
39488 var sw = this.split.el.getWidth();
39495 updateBox : function(box){
39496 if(this.split && !this.collapsed){
39497 var sw = this.split.el.getWidth();
39499 this.split.el.setLeft(box.x);
39500 this.split.el.setTop(box.y);
39501 this.split.el.setHeight(box.height);
39504 if(this.collapsed){
39505 this.updateBody(null, box.height);
39507 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39511 Roo.bootstrap.layout.West = function(config){
39512 config.region = "west";
39513 config.cursor = "w-resize";
39515 Roo.bootstrap.layout.Split.call(this, config);
39517 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39518 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39519 this.split.el.addClass("roo-layout-split-h");
39523 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39524 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39526 onRender: function(ctr, pos)
39528 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39529 var size = this.config.initialSize || this.config.width;
39530 if(typeof size != "undefined"){
39531 this.el.setWidth(size);
39535 getBox : function(){
39536 if(this.collapsed){
39537 return this.collapsedEl.getBox();
39539 var box = this.el.getBox();
39540 if (box.width == 0) {
39541 box.width = this.config.width; // kludge?
39544 box.width += this.split.el.getWidth();
39549 updateBox : function(box){
39550 if(this.split && !this.collapsed){
39551 var sw = this.split.el.getWidth();
39553 this.split.el.setLeft(box.x+box.width);
39554 this.split.el.setTop(box.y);
39555 this.split.el.setHeight(box.height);
39557 if(this.collapsed){
39558 this.updateBody(null, box.height);
39560 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39562 });Roo.namespace("Roo.bootstrap.panel");/*
39564 * Ext JS Library 1.1.1
39565 * Copyright(c) 2006-2007, Ext JS, LLC.
39567 * Originally Released Under LGPL - original licence link has changed is not relivant.
39570 * <script type="text/javascript">
39573 * @class Roo.ContentPanel
39574 * @extends Roo.util.Observable
39575 * A basic ContentPanel element.
39576 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39577 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39578 * @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
39579 * @cfg {Boolean} closable True if the panel can be closed/removed
39580 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39581 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39582 * @cfg {Toolbar} toolbar A toolbar for this panel
39583 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39584 * @cfg {String} title The title for this panel
39585 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39586 * @cfg {String} url Calls {@link #setUrl} with this value
39587 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39588 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39589 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39590 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39591 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39592 * @cfg {Boolean} badges render the badges
39593 * @cfg {String} cls extra classes to use
39594 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39597 * Create a new ContentPanel.
39598 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39599 * @param {String/Object} config A string to set only the title or a config object
39600 * @param {String} content (optional) Set the HTML content for this panel
39601 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39603 Roo.bootstrap.panel.Content = function( config){
39605 this.tpl = config.tpl || false;
39607 var el = config.el;
39608 var content = config.content;
39610 if(config.autoCreate){ // xtype is available if this is called from factory
39613 this.el = Roo.get(el);
39614 if(!this.el && config && config.autoCreate){
39615 if(typeof config.autoCreate == "object"){
39616 if(!config.autoCreate.id){
39617 config.autoCreate.id = config.id||el;
39619 this.el = Roo.DomHelper.append(document.body,
39620 config.autoCreate, true);
39624 cls: (config.cls || '') +
39625 (config.background ? ' bg-' + config.background : '') +
39626 " roo-layout-inactive-content",
39629 if (config.iframe) {
39633 style : 'border: 0px',
39634 src : 'about:blank'
39640 elcfg.html = config.html;
39644 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39645 if (config.iframe) {
39646 this.iframeEl = this.el.select('iframe',true).first();
39651 this.closable = false;
39652 this.loaded = false;
39653 this.active = false;
39656 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39658 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39660 this.wrapEl = this.el; //this.el.wrap();
39662 if (config.toolbar.items) {
39663 ti = config.toolbar.items ;
39664 delete config.toolbar.items ;
39668 this.toolbar.render(this.wrapEl, 'before');
39669 for(var i =0;i < ti.length;i++) {
39670 // Roo.log(['add child', items[i]]);
39671 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39673 this.toolbar.items = nitems;
39674 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39675 delete config.toolbar;
39679 // xtype created footer. - not sure if will work as we normally have to render first..
39680 if (this.footer && !this.footer.el && this.footer.xtype) {
39681 if (!this.wrapEl) {
39682 this.wrapEl = this.el.wrap();
39685 this.footer.container = this.wrapEl.createChild();
39687 this.footer = Roo.factory(this.footer, Roo);
39692 if(typeof config == "string"){
39693 this.title = config;
39695 Roo.apply(this, config);
39699 this.resizeEl = Roo.get(this.resizeEl, true);
39701 this.resizeEl = this.el;
39703 // handle view.xtype
39711 * Fires when this panel is activated.
39712 * @param {Roo.ContentPanel} this
39716 * @event deactivate
39717 * Fires when this panel is activated.
39718 * @param {Roo.ContentPanel} this
39720 "deactivate" : true,
39724 * Fires when this panel is resized if fitToFrame is true.
39725 * @param {Roo.ContentPanel} this
39726 * @param {Number} width The width after any component adjustments
39727 * @param {Number} height The height after any component adjustments
39733 * Fires when this tab is created
39734 * @param {Roo.ContentPanel} this
39745 if(this.autoScroll && !this.iframe){
39746 this.resizeEl.setStyle("overflow", "auto");
39748 // fix randome scrolling
39749 //this.el.on('scroll', function() {
39750 // Roo.log('fix random scolling');
39751 // this.scrollTo('top',0);
39754 content = content || this.content;
39756 this.setContent(content);
39758 if(config && config.url){
39759 this.setUrl(this.url, this.params, this.loadOnce);
39764 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39766 if (this.view && typeof(this.view.xtype) != 'undefined') {
39767 this.view.el = this.el.appendChild(document.createElement("div"));
39768 this.view = Roo.factory(this.view);
39769 this.view.render && this.view.render(false, '');
39773 this.fireEvent('render', this);
39776 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39786 setRegion : function(region){
39787 this.region = region;
39788 this.setActiveClass(region && !this.background);
39792 setActiveClass: function(state)
39795 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39796 this.el.setStyle('position','relative');
39798 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39799 this.el.setStyle('position', 'absolute');
39804 * Returns the toolbar for this Panel if one was configured.
39805 * @return {Roo.Toolbar}
39807 getToolbar : function(){
39808 return this.toolbar;
39811 setActiveState : function(active)
39813 this.active = active;
39814 this.setActiveClass(active);
39816 if(this.fireEvent("deactivate", this) === false){
39821 this.fireEvent("activate", this);
39825 * Updates this panel's element (not for iframe)
39826 * @param {String} content The new content
39827 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39829 setContent : function(content, loadScripts){
39834 this.el.update(content, loadScripts);
39837 ignoreResize : function(w, h){
39838 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39841 this.lastSize = {width: w, height: h};
39846 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39847 * @return {Roo.UpdateManager} The UpdateManager
39849 getUpdateManager : function(){
39853 return this.el.getUpdateManager();
39856 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39857 * Does not work with IFRAME contents
39858 * @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:
39861 url: "your-url.php",
39862 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39863 callback: yourFunction,
39864 scope: yourObject, //(optional scope)
39867 text: "Loading...",
39873 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39874 * 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.
39875 * @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}
39876 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39877 * @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.
39878 * @return {Roo.ContentPanel} this
39886 var um = this.el.getUpdateManager();
39887 um.update.apply(um, arguments);
39893 * 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.
39894 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39895 * @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)
39896 * @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)
39897 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39899 setUrl : function(url, params, loadOnce){
39901 this.iframeEl.dom.src = url;
39905 if(this.refreshDelegate){
39906 this.removeListener("activate", this.refreshDelegate);
39908 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39909 this.on("activate", this.refreshDelegate);
39910 return this.el.getUpdateManager();
39913 _handleRefresh : function(url, params, loadOnce){
39914 if(!loadOnce || !this.loaded){
39915 var updater = this.el.getUpdateManager();
39916 updater.update(url, params, this._setLoaded.createDelegate(this));
39920 _setLoaded : function(){
39921 this.loaded = true;
39925 * Returns this panel's id
39928 getId : function(){
39933 * Returns this panel's element - used by regiosn to add.
39934 * @return {Roo.Element}
39936 getEl : function(){
39937 return this.wrapEl || this.el;
39942 adjustForComponents : function(width, height)
39944 //Roo.log('adjustForComponents ');
39945 if(this.resizeEl != this.el){
39946 width -= this.el.getFrameWidth('lr');
39947 height -= this.el.getFrameWidth('tb');
39950 var te = this.toolbar.getEl();
39951 te.setWidth(width);
39952 height -= te.getHeight();
39955 var te = this.footer.getEl();
39956 te.setWidth(width);
39957 height -= te.getHeight();
39961 if(this.adjustments){
39962 width += this.adjustments[0];
39963 height += this.adjustments[1];
39965 return {"width": width, "height": height};
39968 setSize : function(width, height){
39969 if(this.fitToFrame && !this.ignoreResize(width, height)){
39970 if(this.fitContainer && this.resizeEl != this.el){
39971 this.el.setSize(width, height);
39973 var size = this.adjustForComponents(width, height);
39975 this.iframeEl.setSize(width,height);
39978 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39979 this.fireEvent('resize', this, size.width, size.height);
39986 * Returns this panel's title
39989 getTitle : function(){
39991 if (typeof(this.title) != 'object') {
39996 for (var k in this.title) {
39997 if (!this.title.hasOwnProperty(k)) {
40001 if (k.indexOf('-') >= 0) {
40002 var s = k.split('-');
40003 for (var i = 0; i<s.length; i++) {
40004 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40007 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40014 * Set this panel's title
40015 * @param {String} title
40017 setTitle : function(title){
40018 this.title = title;
40020 this.region.updatePanelTitle(this, title);
40025 * Returns true is this panel was configured to be closable
40026 * @return {Boolean}
40028 isClosable : function(){
40029 return this.closable;
40032 beforeSlide : function(){
40034 this.resizeEl.clip();
40037 afterSlide : function(){
40039 this.resizeEl.unclip();
40043 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40044 * Will fail silently if the {@link #setUrl} method has not been called.
40045 * This does not activate the panel, just updates its content.
40047 refresh : function(){
40048 if(this.refreshDelegate){
40049 this.loaded = false;
40050 this.refreshDelegate();
40055 * Destroys this panel
40057 destroy : function(){
40058 this.el.removeAllListeners();
40059 var tempEl = document.createElement("span");
40060 tempEl.appendChild(this.el.dom);
40061 tempEl.innerHTML = "";
40067 * form - if the content panel contains a form - this is a reference to it.
40068 * @type {Roo.form.Form}
40072 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40073 * This contains a reference to it.
40079 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40089 * @param {Object} cfg Xtype definition of item to add.
40093 getChildContainer: function () {
40094 return this.getEl();
40099 var ret = new Roo.factory(cfg);
40104 if (cfg.xtype.match(/^Form$/)) {
40107 //if (this.footer) {
40108 // el = this.footer.container.insertSibling(false, 'before');
40110 el = this.el.createChild();
40113 this.form = new Roo.form.Form(cfg);
40116 if ( this.form.allItems.length) {
40117 this.form.render(el.dom);
40121 // should only have one of theses..
40122 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40123 // views.. should not be just added - used named prop 'view''
40125 cfg.el = this.el.appendChild(document.createElement("div"));
40128 var ret = new Roo.factory(cfg);
40130 ret.render && ret.render(false, ''); // render blank..
40140 * @class Roo.bootstrap.panel.Grid
40141 * @extends Roo.bootstrap.panel.Content
40143 * Create a new GridPanel.
40144 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40145 * @param {Object} config A the config object
40151 Roo.bootstrap.panel.Grid = function(config)
40155 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40156 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40158 config.el = this.wrapper;
40159 //this.el = this.wrapper;
40161 if (config.container) {
40162 // ctor'ed from a Border/panel.grid
40165 this.wrapper.setStyle("overflow", "hidden");
40166 this.wrapper.addClass('roo-grid-container');
40171 if(config.toolbar){
40172 var tool_el = this.wrapper.createChild();
40173 this.toolbar = Roo.factory(config.toolbar);
40175 if (config.toolbar.items) {
40176 ti = config.toolbar.items ;
40177 delete config.toolbar.items ;
40181 this.toolbar.render(tool_el);
40182 for(var i =0;i < ti.length;i++) {
40183 // Roo.log(['add child', items[i]]);
40184 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40186 this.toolbar.items = nitems;
40188 delete config.toolbar;
40191 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40192 config.grid.scrollBody = true;;
40193 config.grid.monitorWindowResize = false; // turn off autosizing
40194 config.grid.autoHeight = false;
40195 config.grid.autoWidth = false;
40197 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40199 if (config.background) {
40200 // render grid on panel activation (if panel background)
40201 this.on('activate', function(gp) {
40202 if (!gp.grid.rendered) {
40203 gp.grid.render(this.wrapper);
40204 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40209 this.grid.render(this.wrapper);
40210 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40213 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40214 // ??? needed ??? config.el = this.wrapper;
40219 // xtype created footer. - not sure if will work as we normally have to render first..
40220 if (this.footer && !this.footer.el && this.footer.xtype) {
40222 var ctr = this.grid.getView().getFooterPanel(true);
40223 this.footer.dataSource = this.grid.dataSource;
40224 this.footer = Roo.factory(this.footer, Roo);
40225 this.footer.render(ctr);
40235 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40236 getId : function(){
40237 return this.grid.id;
40241 * Returns the grid for this panel
40242 * @return {Roo.bootstrap.Table}
40244 getGrid : function(){
40248 setSize : function(width, height){
40249 if(!this.ignoreResize(width, height)){
40250 var grid = this.grid;
40251 var size = this.adjustForComponents(width, height);
40252 // tfoot is not a footer?
40255 var gridel = grid.getGridEl();
40256 gridel.setSize(size.width, size.height);
40258 var tbd = grid.getGridEl().select('tbody', true).first();
40259 var thd = grid.getGridEl().select('thead',true).first();
40260 var tbf= grid.getGridEl().select('tfoot', true).first();
40263 size.height -= tbf.getHeight();
40266 size.height -= thd.getHeight();
40269 tbd.setSize(size.width, size.height );
40270 // this is for the account management tab -seems to work there.
40271 var thd = grid.getGridEl().select('thead',true).first();
40273 // tbd.setSize(size.width, size.height - thd.getHeight());
40282 beforeSlide : function(){
40283 this.grid.getView().scroller.clip();
40286 afterSlide : function(){
40287 this.grid.getView().scroller.unclip();
40290 destroy : function(){
40291 this.grid.destroy();
40293 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40298 * @class Roo.bootstrap.panel.Nest
40299 * @extends Roo.bootstrap.panel.Content
40301 * Create a new Panel, that can contain a layout.Border.
40304 * @param {Roo.BorderLayout} layout The layout for this panel
40305 * @param {String/Object} config A string to set only the title or a config object
40307 Roo.bootstrap.panel.Nest = function(config)
40309 // construct with only one argument..
40310 /* FIXME - implement nicer consturctors
40311 if (layout.layout) {
40313 layout = config.layout;
40314 delete config.layout;
40316 if (layout.xtype && !layout.getEl) {
40317 // then layout needs constructing..
40318 layout = Roo.factory(layout, Roo);
40322 config.el = config.layout.getEl();
40324 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40326 config.layout.monitorWindowResize = false; // turn off autosizing
40327 this.layout = config.layout;
40328 this.layout.getEl().addClass("roo-layout-nested-layout");
40329 this.layout.parent = this;
40336 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40338 setSize : function(width, height){
40339 if(!this.ignoreResize(width, height)){
40340 var size = this.adjustForComponents(width, height);
40341 var el = this.layout.getEl();
40342 if (size.height < 1) {
40343 el.setWidth(size.width);
40345 el.setSize(size.width, size.height);
40347 var touch = el.dom.offsetWidth;
40348 this.layout.layout();
40349 // ie requires a double layout on the first pass
40350 if(Roo.isIE && !this.initialized){
40351 this.initialized = true;
40352 this.layout.layout();
40357 // activate all subpanels if not currently active..
40359 setActiveState : function(active){
40360 this.active = active;
40361 this.setActiveClass(active);
40364 this.fireEvent("deactivate", this);
40368 this.fireEvent("activate", this);
40369 // not sure if this should happen before or after..
40370 if (!this.layout) {
40371 return; // should not happen..
40374 for (var r in this.layout.regions) {
40375 reg = this.layout.getRegion(r);
40376 if (reg.getActivePanel()) {
40377 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40378 reg.setActivePanel(reg.getActivePanel());
40381 if (!reg.panels.length) {
40384 reg.showPanel(reg.getPanel(0));
40393 * Returns the nested BorderLayout for this panel
40394 * @return {Roo.BorderLayout}
40396 getLayout : function(){
40397 return this.layout;
40401 * Adds a xtype elements to the layout of the nested panel
40405 xtype : 'ContentPanel',
40412 xtype : 'NestedLayoutPanel',
40418 items : [ ... list of content panels or nested layout panels.. ]
40422 * @param {Object} cfg Xtype definition of item to add.
40424 addxtype : function(cfg) {
40425 return this.layout.addxtype(cfg);
40430 * Ext JS Library 1.1.1
40431 * Copyright(c) 2006-2007, Ext JS, LLC.
40433 * Originally Released Under LGPL - original licence link has changed is not relivant.
40436 * <script type="text/javascript">
40439 * @class Roo.TabPanel
40440 * @extends Roo.util.Observable
40441 * A lightweight tab container.
40445 // basic tabs 1, built from existing content
40446 var tabs = new Roo.TabPanel("tabs1");
40447 tabs.addTab("script", "View Script");
40448 tabs.addTab("markup", "View Markup");
40449 tabs.activate("script");
40451 // more advanced tabs, built from javascript
40452 var jtabs = new Roo.TabPanel("jtabs");
40453 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40455 // set up the UpdateManager
40456 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40457 var updater = tab2.getUpdateManager();
40458 updater.setDefaultUrl("ajax1.htm");
40459 tab2.on('activate', updater.refresh, updater, true);
40461 // Use setUrl for Ajax loading
40462 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40463 tab3.setUrl("ajax2.htm", null, true);
40466 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40469 jtabs.activate("jtabs-1");
40472 * Create a new TabPanel.
40473 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40474 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40476 Roo.bootstrap.panel.Tabs = function(config){
40478 * The container element for this TabPanel.
40479 * @type Roo.Element
40481 this.el = Roo.get(config.el);
40484 if(typeof config == "boolean"){
40485 this.tabPosition = config ? "bottom" : "top";
40487 Roo.apply(this, config);
40491 if(this.tabPosition == "bottom"){
40492 // if tabs are at the bottom = create the body first.
40493 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40494 this.el.addClass("roo-tabs-bottom");
40496 // next create the tabs holders
40498 if (this.tabPosition == "west"){
40500 var reg = this.region; // fake it..
40502 if (!reg.mgr.parent) {
40505 reg = reg.mgr.parent.region;
40507 Roo.log("got nest?");
40509 if (reg.mgr.getRegion('west')) {
40510 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40511 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40512 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40513 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40514 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40522 this.stripWrap = Roo.get(this.createStrip(this.el.dom), 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);
40530 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40533 // finally - if tabs are at the top, then create the body last..
40534 if(this.tabPosition != "bottom"){
40535 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40536 * @type Roo.Element
40538 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40539 this.el.addClass("roo-tabs-top");
40543 this.bodyEl.setStyle("position", "relative");
40545 this.active = null;
40546 this.activateDelegate = this.activate.createDelegate(this);
40551 * Fires when the active tab changes
40552 * @param {Roo.TabPanel} this
40553 * @param {Roo.TabPanelItem} activePanel The new active tab
40557 * @event beforetabchange
40558 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40559 * @param {Roo.TabPanel} this
40560 * @param {Object} e Set cancel to true on this object to cancel the tab change
40561 * @param {Roo.TabPanelItem} tab The tab being changed to
40563 "beforetabchange" : true
40566 Roo.EventManager.onWindowResize(this.onResize, this);
40567 this.cpad = this.el.getPadding("lr");
40568 this.hiddenCount = 0;
40571 // toolbar on the tabbar support...
40572 if (this.toolbar) {
40573 alert("no toolbar support yet");
40574 this.toolbar = false;
40576 var tcfg = this.toolbar;
40577 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40578 this.toolbar = new Roo.Toolbar(tcfg);
40579 if (Roo.isSafari) {
40580 var tbl = tcfg.container.child('table', true);
40581 tbl.setAttribute('width', '100%');
40589 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40592 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40594 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40596 tabPosition : "top",
40598 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40600 currentTabWidth : 0,
40602 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40606 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40610 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40612 preferredTabWidth : 175,
40614 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40616 resizeTabs : false,
40618 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40620 monitorResize : true,
40622 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40624 toolbar : false, // set by caller..
40626 region : false, /// set by caller
40628 disableTooltips : true, // not used yet...
40631 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40632 * @param {String} id The id of the div to use <b>or create</b>
40633 * @param {String} text The text for the tab
40634 * @param {String} content (optional) Content to put in the TabPanelItem body
40635 * @param {Boolean} closable (optional) True to create a close icon on the tab
40636 * @return {Roo.TabPanelItem} The created TabPanelItem
40638 addTab : function(id, text, content, closable, tpl)
40640 var item = new Roo.bootstrap.panel.TabItem({
40644 closable : closable,
40647 this.addTabItem(item);
40649 item.setContent(content);
40655 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40656 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40657 * @return {Roo.TabPanelItem}
40659 getTab : function(id){
40660 return this.items[id];
40664 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40665 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40667 hideTab : function(id){
40668 var t = this.items[id];
40671 this.hiddenCount++;
40672 this.autoSizeTabs();
40677 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40678 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40680 unhideTab : function(id){
40681 var t = this.items[id];
40683 t.setHidden(false);
40684 this.hiddenCount--;
40685 this.autoSizeTabs();
40690 * Adds an existing {@link Roo.TabPanelItem}.
40691 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40693 addTabItem : function(item)
40695 this.items[item.id] = item;
40696 this.items.push(item);
40697 this.autoSizeTabs();
40698 // if(this.resizeTabs){
40699 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40700 // this.autoSizeTabs();
40702 // item.autoSize();
40707 * Removes a {@link Roo.TabPanelItem}.
40708 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40710 removeTab : function(id){
40711 var items = this.items;
40712 var tab = items[id];
40713 if(!tab) { return; }
40714 var index = items.indexOf(tab);
40715 if(this.active == tab && items.length > 1){
40716 var newTab = this.getNextAvailable(index);
40721 this.stripEl.dom.removeChild(tab.pnode.dom);
40722 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40723 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40725 items.splice(index, 1);
40726 delete this.items[tab.id];
40727 tab.fireEvent("close", tab);
40728 tab.purgeListeners();
40729 this.autoSizeTabs();
40732 getNextAvailable : function(start){
40733 var items = this.items;
40735 // look for a next tab that will slide over to
40736 // replace the one being removed
40737 while(index < items.length){
40738 var item = items[++index];
40739 if(item && !item.isHidden()){
40743 // if one isn't found select the previous tab (on the left)
40746 var item = items[--index];
40747 if(item && !item.isHidden()){
40755 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40756 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40758 disableTab : function(id){
40759 var tab = this.items[id];
40760 if(tab && this.active != tab){
40766 * Enables a {@link Roo.TabPanelItem} that is disabled.
40767 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40769 enableTab : function(id){
40770 var tab = this.items[id];
40775 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40776 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40777 * @return {Roo.TabPanelItem} The TabPanelItem.
40779 activate : function(id)
40781 //Roo.log('activite:' + id);
40783 var tab = this.items[id];
40787 if(tab == this.active || tab.disabled){
40791 this.fireEvent("beforetabchange", this, e, tab);
40792 if(e.cancel !== true && !tab.disabled){
40794 this.active.hide();
40796 this.active = this.items[id];
40797 this.active.show();
40798 this.fireEvent("tabchange", this, this.active);
40804 * Gets the active {@link Roo.TabPanelItem}.
40805 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40807 getActiveTab : function(){
40808 return this.active;
40812 * Updates the tab body element to fit the height of the container element
40813 * for overflow scrolling
40814 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40816 syncHeight : function(targetHeight){
40817 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40818 var bm = this.bodyEl.getMargins();
40819 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40820 this.bodyEl.setHeight(newHeight);
40824 onResize : function(){
40825 if(this.monitorResize){
40826 this.autoSizeTabs();
40831 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40833 beginUpdate : function(){
40834 this.updating = true;
40838 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40840 endUpdate : function(){
40841 this.updating = false;
40842 this.autoSizeTabs();
40846 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40848 autoSizeTabs : function()
40850 var count = this.items.length;
40851 var vcount = count - this.hiddenCount;
40854 this.stripEl.hide();
40856 this.stripEl.show();
40859 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40864 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40865 var availWidth = Math.floor(w / vcount);
40866 var b = this.stripBody;
40867 if(b.getWidth() > w){
40868 var tabs = this.items;
40869 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40870 if(availWidth < this.minTabWidth){
40871 /*if(!this.sleft){ // incomplete scrolling code
40872 this.createScrollButtons();
40875 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40878 if(this.currentTabWidth < this.preferredTabWidth){
40879 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40885 * Returns the number of tabs in this TabPanel.
40888 getCount : function(){
40889 return this.items.length;
40893 * Resizes all the tabs to the passed width
40894 * @param {Number} The new width
40896 setTabWidth : function(width){
40897 this.currentTabWidth = width;
40898 for(var i = 0, len = this.items.length; i < len; i++) {
40899 if(!this.items[i].isHidden()) {
40900 this.items[i].setWidth(width);
40906 * Destroys this TabPanel
40907 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40909 destroy : function(removeEl){
40910 Roo.EventManager.removeResizeListener(this.onResize, this);
40911 for(var i = 0, len = this.items.length; i < len; i++){
40912 this.items[i].purgeListeners();
40914 if(removeEl === true){
40915 this.el.update("");
40920 createStrip : function(container)
40922 var strip = document.createElement("nav");
40923 strip.className = Roo.bootstrap.version == 4 ?
40924 "navbar-light bg-light" :
40925 "navbar navbar-default"; //"x-tabs-wrap";
40926 container.appendChild(strip);
40930 createStripList : function(strip)
40932 // div wrapper for retard IE
40933 // returns the "tr" element.
40934 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40935 //'<div class="x-tabs-strip-wrap">'+
40936 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40937 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40938 return strip.firstChild; //.firstChild.firstChild.firstChild;
40940 createBody : function(container)
40942 var body = document.createElement("div");
40943 Roo.id(body, "tab-body");
40944 //Roo.fly(body).addClass("x-tabs-body");
40945 Roo.fly(body).addClass("tab-content");
40946 container.appendChild(body);
40949 createItemBody :function(bodyEl, id){
40950 var body = Roo.getDom(id);
40952 body = document.createElement("div");
40955 //Roo.fly(body).addClass("x-tabs-item-body");
40956 Roo.fly(body).addClass("tab-pane");
40957 bodyEl.insertBefore(body, bodyEl.firstChild);
40961 createStripElements : function(stripEl, text, closable, tpl)
40963 var td = document.createElement("li"); // was td..
40964 td.className = 'nav-item';
40966 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40969 stripEl.appendChild(td);
40971 td.className = "x-tabs-closable";
40972 if(!this.closeTpl){
40973 this.closeTpl = new Roo.Template(
40974 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40975 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40976 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40979 var el = this.closeTpl.overwrite(td, {"text": text});
40980 var close = el.getElementsByTagName("div")[0];
40981 var inner = el.getElementsByTagName("em")[0];
40982 return {"el": el, "close": close, "inner": inner};
40985 // not sure what this is..
40986 // if(!this.tabTpl){
40987 //this.tabTpl = new Roo.Template(
40988 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40989 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40991 // this.tabTpl = new Roo.Template(
40992 // '<a href="#">' +
40993 // '<span unselectable="on"' +
40994 // (this.disableTooltips ? '' : ' title="{text}"') +
40995 // ' >{text}</span></a>'
41001 var template = tpl || this.tabTpl || false;
41004 template = new Roo.Template(
41005 Roo.bootstrap.version == 4 ?
41007 '<a class="nav-link" href="#" unselectable="on"' +
41008 (this.disableTooltips ? '' : ' title="{text}"') +
41011 '<a class="nav-link" href="#">' +
41012 '<span unselectable="on"' +
41013 (this.disableTooltips ? '' : ' title="{text}"') +
41014 ' >{text}</span></a>'
41019 switch (typeof(template)) {
41023 template = new Roo.Template(template);
41029 var el = template.overwrite(td, {"text": text});
41031 var inner = el.getElementsByTagName("span")[0];
41033 return {"el": el, "inner": inner};
41041 * @class Roo.TabPanelItem
41042 * @extends Roo.util.Observable
41043 * Represents an individual item (tab plus body) in a TabPanel.
41044 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41045 * @param {String} id The id of this TabPanelItem
41046 * @param {String} text The text for the tab of this TabPanelItem
41047 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41049 Roo.bootstrap.panel.TabItem = function(config){
41051 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41052 * @type Roo.TabPanel
41054 this.tabPanel = config.panel;
41056 * The id for this TabPanelItem
41059 this.id = config.id;
41061 this.disabled = false;
41063 this.text = config.text;
41065 this.loaded = false;
41066 this.closable = config.closable;
41069 * The body element for this TabPanelItem.
41070 * @type Roo.Element
41072 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41073 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41074 this.bodyEl.setStyle("display", "block");
41075 this.bodyEl.setStyle("zoom", "1");
41076 //this.hideAction();
41078 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41080 this.el = Roo.get(els.el);
41081 this.inner = Roo.get(els.inner, true);
41082 this.textEl = Roo.bootstrap.version == 4 ?
41083 this.el : Roo.get(this.el.dom.firstChild, true);
41085 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41086 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41089 // this.el.on("mousedown", this.onTabMouseDown, this);
41090 this.el.on("click", this.onTabClick, this);
41092 if(config.closable){
41093 var c = Roo.get(els.close, true);
41094 c.dom.title = this.closeText;
41095 c.addClassOnOver("close-over");
41096 c.on("click", this.closeClick, this);
41102 * Fires when this tab becomes the active tab.
41103 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41104 * @param {Roo.TabPanelItem} this
41108 * @event beforeclose
41109 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41110 * @param {Roo.TabPanelItem} this
41111 * @param {Object} e Set cancel to true on this object to cancel the close.
41113 "beforeclose": true,
41116 * Fires when this tab is closed.
41117 * @param {Roo.TabPanelItem} this
41121 * @event deactivate
41122 * Fires when this tab is no longer the active tab.
41123 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41124 * @param {Roo.TabPanelItem} this
41126 "deactivate" : true
41128 this.hidden = false;
41130 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41133 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41135 purgeListeners : function(){
41136 Roo.util.Observable.prototype.purgeListeners.call(this);
41137 this.el.removeAllListeners();
41140 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41143 this.status_node.addClass("active");
41146 this.tabPanel.stripWrap.repaint();
41148 this.fireEvent("activate", this.tabPanel, this);
41152 * Returns true if this tab is the active tab.
41153 * @return {Boolean}
41155 isActive : function(){
41156 return this.tabPanel.getActiveTab() == this;
41160 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41163 this.status_node.removeClass("active");
41165 this.fireEvent("deactivate", this.tabPanel, this);
41168 hideAction : function(){
41169 this.bodyEl.hide();
41170 this.bodyEl.setStyle("position", "absolute");
41171 this.bodyEl.setLeft("-20000px");
41172 this.bodyEl.setTop("-20000px");
41175 showAction : function(){
41176 this.bodyEl.setStyle("position", "relative");
41177 this.bodyEl.setTop("");
41178 this.bodyEl.setLeft("");
41179 this.bodyEl.show();
41183 * Set the tooltip for the tab.
41184 * @param {String} tooltip The tab's tooltip
41186 setTooltip : function(text){
41187 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41188 this.textEl.dom.qtip = text;
41189 this.textEl.dom.removeAttribute('title');
41191 this.textEl.dom.title = text;
41195 onTabClick : function(e){
41196 e.preventDefault();
41197 this.tabPanel.activate(this.id);
41200 onTabMouseDown : function(e){
41201 e.preventDefault();
41202 this.tabPanel.activate(this.id);
41205 getWidth : function(){
41206 return this.inner.getWidth();
41209 setWidth : function(width){
41210 var iwidth = width - this.linode.getPadding("lr");
41211 this.inner.setWidth(iwidth);
41212 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41213 this.linode.setWidth(width);
41217 * Show or hide the tab
41218 * @param {Boolean} hidden True to hide or false to show.
41220 setHidden : function(hidden){
41221 this.hidden = hidden;
41222 this.linode.setStyle("display", hidden ? "none" : "");
41226 * Returns true if this tab is "hidden"
41227 * @return {Boolean}
41229 isHidden : function(){
41230 return this.hidden;
41234 * Returns the text for this tab
41237 getText : function(){
41241 autoSize : function(){
41242 //this.el.beginMeasure();
41243 this.textEl.setWidth(1);
41245 * #2804 [new] Tabs in Roojs
41246 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41248 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41249 //this.el.endMeasure();
41253 * Sets the text for the tab (Note: this also sets the tooltip text)
41254 * @param {String} text The tab's text and tooltip
41256 setText : function(text){
41258 this.textEl.update(text);
41259 this.setTooltip(text);
41260 //if(!this.tabPanel.resizeTabs){
41261 // this.autoSize();
41265 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41267 activate : function(){
41268 this.tabPanel.activate(this.id);
41272 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41274 disable : function(){
41275 if(this.tabPanel.active != this){
41276 this.disabled = true;
41277 this.status_node.addClass("disabled");
41282 * Enables this TabPanelItem if it was previously disabled.
41284 enable : function(){
41285 this.disabled = false;
41286 this.status_node.removeClass("disabled");
41290 * Sets the content for this TabPanelItem.
41291 * @param {String} content The content
41292 * @param {Boolean} loadScripts true to look for and load scripts
41294 setContent : function(content, loadScripts){
41295 this.bodyEl.update(content, loadScripts);
41299 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41300 * @return {Roo.UpdateManager} The UpdateManager
41302 getUpdateManager : function(){
41303 return this.bodyEl.getUpdateManager();
41307 * Set a URL to be used to load the content for this TabPanelItem.
41308 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41309 * @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)
41310 * @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)
41311 * @return {Roo.UpdateManager} The UpdateManager
41313 setUrl : function(url, params, loadOnce){
41314 if(this.refreshDelegate){
41315 this.un('activate', this.refreshDelegate);
41317 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41318 this.on("activate", this.refreshDelegate);
41319 return this.bodyEl.getUpdateManager();
41323 _handleRefresh : function(url, params, loadOnce){
41324 if(!loadOnce || !this.loaded){
41325 var updater = this.bodyEl.getUpdateManager();
41326 updater.update(url, params, this._setLoaded.createDelegate(this));
41331 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41332 * Will fail silently if the setUrl method has not been called.
41333 * This does not activate the panel, just updates its content.
41335 refresh : function(){
41336 if(this.refreshDelegate){
41337 this.loaded = false;
41338 this.refreshDelegate();
41343 _setLoaded : function(){
41344 this.loaded = true;
41348 closeClick : function(e){
41351 this.fireEvent("beforeclose", this, o);
41352 if(o.cancel !== true){
41353 this.tabPanel.removeTab(this.id);
41357 * The text displayed in the tooltip for the close icon.
41360 closeText : "Close this tab"
41363 * This script refer to:
41364 * Title: International Telephone Input
41365 * Author: Jack O'Connor
41366 * Code version: v12.1.12
41367 * Availability: https://github.com/jackocnr/intl-tel-input.git
41370 Roo.bootstrap.PhoneInputData = function() {
41373 "Afghanistan (افغانستان)",
41378 "Albania (Shqipëri)",
41383 "Algeria (الجزائر)",
41408 "Antigua and Barbuda",
41418 "Armenia (Հայաստան)",
41434 "Austria (Österreich)",
41439 "Azerbaijan (Azərbaycan)",
41449 "Bahrain (البحرين)",
41454 "Bangladesh (বাংলাদেশ)",
41464 "Belarus (Беларусь)",
41469 "Belgium (België)",
41499 "Bosnia and Herzegovina (Босна и Херцеговина)",
41514 "British Indian Ocean Territory",
41519 "British Virgin Islands",
41529 "Bulgaria (България)",
41539 "Burundi (Uburundi)",
41544 "Cambodia (កម្ពុជា)",
41549 "Cameroon (Cameroun)",
41558 ["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"]
41561 "Cape Verde (Kabu Verdi)",
41566 "Caribbean Netherlands",
41577 "Central African Republic (République centrafricaine)",
41597 "Christmas Island",
41603 "Cocos (Keeling) Islands",
41614 "Comoros (جزر القمر)",
41619 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41624 "Congo (Republic) (Congo-Brazzaville)",
41644 "Croatia (Hrvatska)",
41665 "Czech Republic (Česká republika)",
41670 "Denmark (Danmark)",
41685 "Dominican Republic (República Dominicana)",
41689 ["809", "829", "849"]
41707 "Equatorial Guinea (Guinea Ecuatorial)",
41727 "Falkland Islands (Islas Malvinas)",
41732 "Faroe Islands (Føroyar)",
41753 "French Guiana (Guyane française)",
41758 "French Polynesia (Polynésie française)",
41773 "Georgia (საქართველო)",
41778 "Germany (Deutschland)",
41798 "Greenland (Kalaallit Nunaat)",
41835 "Guinea-Bissau (Guiné Bissau)",
41860 "Hungary (Magyarország)",
41865 "Iceland (Ísland)",
41885 "Iraq (العراق)",
41901 "Israel (ישראל)",
41928 "Jordan (الأردن)",
41933 "Kazakhstan (Казахстан)",
41954 "Kuwait (الكويت)",
41959 "Kyrgyzstan (Кыргызстан)",
41969 "Latvia (Latvija)",
41974 "Lebanon (لبنان)",
41989 "Libya (ليبيا)",
41999 "Lithuania (Lietuva)",
42014 "Macedonia (FYROM) (Македонија)",
42019 "Madagascar (Madagasikara)",
42049 "Marshall Islands",
42059 "Mauritania (موريتانيا)",
42064 "Mauritius (Moris)",
42085 "Moldova (Republica Moldova)",
42095 "Mongolia (Монгол)",
42100 "Montenegro (Crna Gora)",
42110 "Morocco (المغرب)",
42116 "Mozambique (Moçambique)",
42121 "Myanmar (Burma) (မြန်မာ)",
42126 "Namibia (Namibië)",
42141 "Netherlands (Nederland)",
42146 "New Caledonia (Nouvelle-Calédonie)",
42181 "North Korea (조선 민주주의 인민 공화국)",
42186 "Northern Mariana Islands",
42202 "Pakistan (پاکستان)",
42212 "Palestine (فلسطين)",
42222 "Papua New Guinea",
42264 "Réunion (La Réunion)",
42270 "Romania (România)",
42286 "Saint Barthélemy",
42297 "Saint Kitts and Nevis",
42307 "Saint Martin (Saint-Martin (partie française))",
42313 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42318 "Saint Vincent and the Grenadines",
42333 "São Tomé and Príncipe (São Tomé e Príncipe)",
42338 "Saudi Arabia (المملكة العربية السعودية)",
42343 "Senegal (Sénégal)",
42373 "Slovakia (Slovensko)",
42378 "Slovenia (Slovenija)",
42388 "Somalia (Soomaaliya)",
42398 "South Korea (대한민국)",
42403 "South Sudan (جنوب السودان)",
42413 "Sri Lanka (ශ්රී ලංකාව)",
42418 "Sudan (السودان)",
42428 "Svalbard and Jan Mayen",
42439 "Sweden (Sverige)",
42444 "Switzerland (Schweiz)",
42449 "Syria (سوريا)",
42494 "Trinidad and Tobago",
42499 "Tunisia (تونس)",
42504 "Turkey (Türkiye)",
42514 "Turks and Caicos Islands",
42524 "U.S. Virgin Islands",
42534 "Ukraine (Україна)",
42539 "United Arab Emirates (الإمارات العربية المتحدة)",
42561 "Uzbekistan (Oʻzbekiston)",
42571 "Vatican City (Città del Vaticano)",
42582 "Vietnam (Việt Nam)",
42587 "Wallis and Futuna (Wallis-et-Futuna)",
42592 "Western Sahara (الصحراء الغربية)",
42598 "Yemen (اليمن)",
42622 * This script refer to:
42623 * Title: International Telephone Input
42624 * Author: Jack O'Connor
42625 * Code version: v12.1.12
42626 * Availability: https://github.com/jackocnr/intl-tel-input.git
42630 * @class Roo.bootstrap.PhoneInput
42631 * @extends Roo.bootstrap.TriggerField
42632 * An input with International dial-code selection
42634 * @cfg {String} defaultDialCode default '+852'
42635 * @cfg {Array} preferedCountries default []
42638 * Create a new PhoneInput.
42639 * @param {Object} config Configuration options
42642 Roo.bootstrap.PhoneInput = function(config) {
42643 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42646 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42648 listWidth: undefined,
42650 selectedClass: 'active',
42652 invalidClass : "has-warning",
42654 validClass: 'has-success',
42656 allowed: '0123456789',
42661 * @cfg {String} defaultDialCode The default dial code when initializing the input
42663 defaultDialCode: '+852',
42666 * @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
42668 preferedCountries: false,
42670 getAutoCreate : function()
42672 var data = Roo.bootstrap.PhoneInputData();
42673 var align = this.labelAlign || this.parentLabelAlign();
42676 this.allCountries = [];
42677 this.dialCodeMapping = [];
42679 for (var i = 0; i < data.length; i++) {
42681 this.allCountries[i] = {
42685 priority: c[3] || 0,
42686 areaCodes: c[4] || null
42688 this.dialCodeMapping[c[2]] = {
42691 priority: c[3] || 0,
42692 areaCodes: c[4] || null
42704 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42705 maxlength: this.max_length,
42706 cls : 'form-control tel-input',
42707 autocomplete: 'new-password'
42710 var hiddenInput = {
42713 cls: 'hidden-tel-input'
42717 hiddenInput.name = this.name;
42720 if (this.disabled) {
42721 input.disabled = true;
42724 var flag_container = {
42741 cls: this.hasFeedback ? 'has-feedback' : '',
42747 cls: 'dial-code-holder',
42754 cls: 'roo-select2-container input-group',
42761 if (this.fieldLabel.length) {
42764 tooltip: 'This field is required'
42770 cls: 'control-label',
42776 html: this.fieldLabel
42779 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42785 if(this.indicatorpos == 'right') {
42786 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42793 if(align == 'left') {
42801 if(this.labelWidth > 12){
42802 label.style = "width: " + this.labelWidth + 'px';
42804 if(this.labelWidth < 13 && this.labelmd == 0){
42805 this.labelmd = this.labelWidth;
42807 if(this.labellg > 0){
42808 label.cls += ' col-lg-' + this.labellg;
42809 input.cls += ' col-lg-' + (12 - this.labellg);
42811 if(this.labelmd > 0){
42812 label.cls += ' col-md-' + this.labelmd;
42813 container.cls += ' col-md-' + (12 - this.labelmd);
42815 if(this.labelsm > 0){
42816 label.cls += ' col-sm-' + this.labelsm;
42817 container.cls += ' col-sm-' + (12 - this.labelsm);
42819 if(this.labelxs > 0){
42820 label.cls += ' col-xs-' + this.labelxs;
42821 container.cls += ' col-xs-' + (12 - this.labelxs);
42831 var settings = this;
42833 ['xs','sm','md','lg'].map(function(size){
42834 if (settings[size]) {
42835 cfg.cls += ' col-' + size + '-' + settings[size];
42839 this.store = new Roo.data.Store({
42840 proxy : new Roo.data.MemoryProxy({}),
42841 reader : new Roo.data.JsonReader({
42852 'name' : 'dialCode',
42856 'name' : 'priority',
42860 'name' : 'areaCodes',
42867 if(!this.preferedCountries) {
42868 this.preferedCountries = [
42875 var p = this.preferedCountries.reverse();
42878 for (var i = 0; i < p.length; i++) {
42879 for (var j = 0; j < this.allCountries.length; j++) {
42880 if(this.allCountries[j].iso2 == p[i]) {
42881 var t = this.allCountries[j];
42882 this.allCountries.splice(j,1);
42883 this.allCountries.unshift(t);
42889 this.store.proxy.data = {
42891 data: this.allCountries
42897 initEvents : function()
42900 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42902 this.indicator = this.indicatorEl();
42903 this.flag = this.flagEl();
42904 this.dialCodeHolder = this.dialCodeHolderEl();
42906 this.trigger = this.el.select('div.flag-box',true).first();
42907 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42912 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42913 _this.list.setWidth(lw);
42916 this.list.on('mouseover', this.onViewOver, this);
42917 this.list.on('mousemove', this.onViewMove, this);
42918 this.inputEl().on("keyup", this.onKeyUp, this);
42919 this.inputEl().on("keypress", this.onKeyPress, this);
42921 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42923 this.view = new Roo.View(this.list, this.tpl, {
42924 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42927 this.view.on('click', this.onViewClick, this);
42928 this.setValue(this.defaultDialCode);
42931 onTriggerClick : function(e)
42933 Roo.log('trigger click');
42938 if(this.isExpanded()){
42940 this.hasFocus = false;
42942 this.store.load({});
42943 this.hasFocus = true;
42948 isExpanded : function()
42950 return this.list.isVisible();
42953 collapse : function()
42955 if(!this.isExpanded()){
42959 Roo.get(document).un('mousedown', this.collapseIf, this);
42960 Roo.get(document).un('mousewheel', this.collapseIf, this);
42961 this.fireEvent('collapse', this);
42965 expand : function()
42969 if(this.isExpanded() || !this.hasFocus){
42973 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42974 this.list.setWidth(lw);
42977 this.restrictHeight();
42979 Roo.get(document).on('mousedown', this.collapseIf, this);
42980 Roo.get(document).on('mousewheel', this.collapseIf, this);
42982 this.fireEvent('expand', this);
42985 restrictHeight : function()
42987 this.list.alignTo(this.inputEl(), this.listAlign);
42988 this.list.alignTo(this.inputEl(), this.listAlign);
42991 onViewOver : function(e, t)
42993 if(this.inKeyMode){
42996 var item = this.view.findItemFromChild(t);
42999 var index = this.view.indexOf(item);
43000 this.select(index, false);
43005 onViewClick : function(view, doFocus, el, e)
43007 var index = this.view.getSelectedIndexes()[0];
43009 var r = this.store.getAt(index);
43012 this.onSelect(r, index);
43014 if(doFocus !== false && !this.blockFocus){
43015 this.inputEl().focus();
43019 onViewMove : function(e, t)
43021 this.inKeyMode = false;
43024 select : function(index, scrollIntoView)
43026 this.selectedIndex = index;
43027 this.view.select(index);
43028 if(scrollIntoView !== false){
43029 var el = this.view.getNode(index);
43031 this.list.scrollChildIntoView(el, false);
43036 createList : function()
43038 this.list = Roo.get(document.body).createChild({
43040 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43041 style: 'display:none'
43044 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43047 collapseIf : function(e)
43049 var in_combo = e.within(this.el);
43050 var in_list = e.within(this.list);
43051 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43053 if (in_combo || in_list || is_list) {
43059 onSelect : function(record, index)
43061 if(this.fireEvent('beforeselect', this, record, index) !== false){
43063 this.setFlagClass(record.data.iso2);
43064 this.setDialCode(record.data.dialCode);
43065 this.hasFocus = false;
43067 this.fireEvent('select', this, record, index);
43071 flagEl : function()
43073 var flag = this.el.select('div.flag',true).first();
43080 dialCodeHolderEl : function()
43082 var d = this.el.select('input.dial-code-holder',true).first();
43089 setDialCode : function(v)
43091 this.dialCodeHolder.dom.value = '+'+v;
43094 setFlagClass : function(n)
43096 this.flag.dom.className = 'flag '+n;
43099 getValue : function()
43101 var v = this.inputEl().getValue();
43102 if(this.dialCodeHolder) {
43103 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43108 setValue : function(v)
43110 var d = this.getDialCode(v);
43112 //invalid dial code
43113 if(v.length == 0 || !d || d.length == 0) {
43115 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43116 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43122 this.setFlagClass(this.dialCodeMapping[d].iso2);
43123 this.setDialCode(d);
43124 this.inputEl().dom.value = v.replace('+'+d,'');
43125 this.hiddenEl().dom.value = this.getValue();
43130 getDialCode : function(v)
43134 if (v.length == 0) {
43135 return this.dialCodeHolder.dom.value;
43139 if (v.charAt(0) != "+") {
43142 var numericChars = "";
43143 for (var i = 1; i < v.length; i++) {
43144 var c = v.charAt(i);
43147 if (this.dialCodeMapping[numericChars]) {
43148 dialCode = v.substr(1, i);
43150 if (numericChars.length == 4) {
43160 this.setValue(this.defaultDialCode);
43164 hiddenEl : function()
43166 return this.el.select('input.hidden-tel-input',true).first();
43169 // after setting val
43170 onKeyUp : function(e){
43171 this.setValue(this.getValue());
43174 onKeyPress : function(e){
43175 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43182 * @class Roo.bootstrap.MoneyField
43183 * @extends Roo.bootstrap.ComboBox
43184 * Bootstrap MoneyField class
43187 * Create a new MoneyField.
43188 * @param {Object} config Configuration options
43191 Roo.bootstrap.MoneyField = function(config) {
43193 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43197 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43200 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43202 allowDecimals : true,
43204 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43206 decimalSeparator : ".",
43208 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43210 decimalPrecision : 0,
43212 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43214 allowNegative : true,
43216 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43220 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43222 minValue : Number.NEGATIVE_INFINITY,
43224 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43226 maxValue : Number.MAX_VALUE,
43228 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43230 minText : "The minimum value for this field is {0}",
43232 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43234 maxText : "The maximum value for this field is {0}",
43236 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43237 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43239 nanText : "{0} is not a valid number",
43241 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43245 * @cfg {String} defaults currency of the MoneyField
43246 * value should be in lkey
43248 defaultCurrency : false,
43250 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43252 thousandsDelimiter : false,
43254 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43265 getAutoCreate : function()
43267 var align = this.labelAlign || this.parentLabelAlign();
43279 cls : 'form-control roo-money-amount-input',
43280 autocomplete: 'new-password'
43283 var hiddenInput = {
43287 cls: 'hidden-number-input'
43290 if(this.max_length) {
43291 input.maxlength = this.max_length;
43295 hiddenInput.name = this.name;
43298 if (this.disabled) {
43299 input.disabled = true;
43302 var clg = 12 - this.inputlg;
43303 var cmd = 12 - this.inputmd;
43304 var csm = 12 - this.inputsm;
43305 var cxs = 12 - this.inputxs;
43309 cls : 'row roo-money-field',
43313 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43317 cls: 'roo-select2-container input-group',
43321 cls : 'form-control roo-money-currency-input',
43322 autocomplete: 'new-password',
43324 name : this.currencyName
43328 cls : 'input-group-addon',
43342 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43346 cls: this.hasFeedback ? 'has-feedback' : '',
43357 if (this.fieldLabel.length) {
43360 tooltip: 'This field is required'
43366 cls: 'control-label',
43372 html: this.fieldLabel
43375 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43381 if(this.indicatorpos == 'right') {
43382 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43389 if(align == 'left') {
43397 if(this.labelWidth > 12){
43398 label.style = "width: " + this.labelWidth + 'px';
43400 if(this.labelWidth < 13 && this.labelmd == 0){
43401 this.labelmd = this.labelWidth;
43403 if(this.labellg > 0){
43404 label.cls += ' col-lg-' + this.labellg;
43405 input.cls += ' col-lg-' + (12 - this.labellg);
43407 if(this.labelmd > 0){
43408 label.cls += ' col-md-' + this.labelmd;
43409 container.cls += ' col-md-' + (12 - this.labelmd);
43411 if(this.labelsm > 0){
43412 label.cls += ' col-sm-' + this.labelsm;
43413 container.cls += ' col-sm-' + (12 - this.labelsm);
43415 if(this.labelxs > 0){
43416 label.cls += ' col-xs-' + this.labelxs;
43417 container.cls += ' col-xs-' + (12 - this.labelxs);
43428 var settings = this;
43430 ['xs','sm','md','lg'].map(function(size){
43431 if (settings[size]) {
43432 cfg.cls += ' col-' + size + '-' + settings[size];
43439 initEvents : function()
43441 this.indicator = this.indicatorEl();
43443 this.initCurrencyEvent();
43445 this.initNumberEvent();
43448 initCurrencyEvent : function()
43451 throw "can not find store for combo";
43454 this.store = Roo.factory(this.store, Roo.data);
43455 this.store.parent = this;
43459 this.triggerEl = this.el.select('.input-group-addon', true).first();
43461 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43466 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43467 _this.list.setWidth(lw);
43470 this.list.on('mouseover', this.onViewOver, this);
43471 this.list.on('mousemove', this.onViewMove, this);
43472 this.list.on('scroll', this.onViewScroll, this);
43475 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43478 this.view = new Roo.View(this.list, this.tpl, {
43479 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43482 this.view.on('click', this.onViewClick, this);
43484 this.store.on('beforeload', this.onBeforeLoad, this);
43485 this.store.on('load', this.onLoad, this);
43486 this.store.on('loadexception', this.onLoadException, this);
43488 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43489 "up" : function(e){
43490 this.inKeyMode = true;
43494 "down" : function(e){
43495 if(!this.isExpanded()){
43496 this.onTriggerClick();
43498 this.inKeyMode = true;
43503 "enter" : function(e){
43506 if(this.fireEvent("specialkey", this, e)){
43507 this.onViewClick(false);
43513 "esc" : function(e){
43517 "tab" : function(e){
43520 if(this.fireEvent("specialkey", this, e)){
43521 this.onViewClick(false);
43529 doRelay : function(foo, bar, hname){
43530 if(hname == 'down' || this.scope.isExpanded()){
43531 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43539 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43543 initNumberEvent : function(e)
43545 this.inputEl().on("keydown" , this.fireKey, this);
43546 this.inputEl().on("focus", this.onFocus, this);
43547 this.inputEl().on("blur", this.onBlur, this);
43549 this.inputEl().relayEvent('keyup', this);
43551 if(this.indicator){
43552 this.indicator.addClass('invisible');
43555 this.originalValue = this.getValue();
43557 if(this.validationEvent == 'keyup'){
43558 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43559 this.inputEl().on('keyup', this.filterValidation, this);
43561 else if(this.validationEvent !== false){
43562 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43565 if(this.selectOnFocus){
43566 this.on("focus", this.preFocus, this);
43569 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43570 this.inputEl().on("keypress", this.filterKeys, this);
43572 this.inputEl().relayEvent('keypress', this);
43575 var allowed = "0123456789";
43577 if(this.allowDecimals){
43578 allowed += this.decimalSeparator;
43581 if(this.allowNegative){
43585 if(this.thousandsDelimiter) {
43589 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43591 var keyPress = function(e){
43593 var k = e.getKey();
43595 var c = e.getCharCode();
43598 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43599 allowed.indexOf(String.fromCharCode(c)) === -1
43605 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43609 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43614 this.inputEl().on("keypress", keyPress, this);
43618 onTriggerClick : function(e)
43625 this.loadNext = false;
43627 if(this.isExpanded()){
43632 this.hasFocus = true;
43634 if(this.triggerAction == 'all') {
43635 this.doQuery(this.allQuery, true);
43639 this.doQuery(this.getRawValue());
43642 getCurrency : function()
43644 var v = this.currencyEl().getValue();
43649 restrictHeight : function()
43651 this.list.alignTo(this.currencyEl(), this.listAlign);
43652 this.list.alignTo(this.currencyEl(), this.listAlign);
43655 onViewClick : function(view, doFocus, el, e)
43657 var index = this.view.getSelectedIndexes()[0];
43659 var r = this.store.getAt(index);
43662 this.onSelect(r, index);
43666 onSelect : function(record, index){
43668 if(this.fireEvent('beforeselect', this, record, index) !== false){
43670 this.setFromCurrencyData(index > -1 ? record.data : false);
43674 this.fireEvent('select', this, record, index);
43678 setFromCurrencyData : function(o)
43682 this.lastCurrency = o;
43684 if (this.currencyField) {
43685 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43687 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43690 this.lastSelectionText = currency;
43692 //setting default currency
43693 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43694 this.setCurrency(this.defaultCurrency);
43698 this.setCurrency(currency);
43701 setFromData : function(o)
43705 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43707 this.setFromCurrencyData(c);
43712 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43714 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43717 this.setValue(value);
43721 setCurrency : function(v)
43723 this.currencyValue = v;
43726 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43731 setValue : function(v)
43733 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43739 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43741 this.inputEl().dom.value = (v == '') ? '' :
43742 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43744 if(!this.allowZero && v === '0') {
43745 this.hiddenEl().dom.value = '';
43746 this.inputEl().dom.value = '';
43753 getRawValue : function()
43755 var v = this.inputEl().getValue();
43760 getValue : function()
43762 return this.fixPrecision(this.parseValue(this.getRawValue()));
43765 parseValue : function(value)
43767 if(this.thousandsDelimiter) {
43769 r = new RegExp(",", "g");
43770 value = value.replace(r, "");
43773 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43774 return isNaN(value) ? '' : value;
43778 fixPrecision : function(value)
43780 if(this.thousandsDelimiter) {
43782 r = new RegExp(",", "g");
43783 value = value.replace(r, "");
43786 var nan = isNaN(value);
43788 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43789 return nan ? '' : value;
43791 return parseFloat(value).toFixed(this.decimalPrecision);
43794 decimalPrecisionFcn : function(v)
43796 return Math.floor(v);
43799 validateValue : function(value)
43801 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43805 var num = this.parseValue(value);
43808 this.markInvalid(String.format(this.nanText, value));
43812 if(num < this.minValue){
43813 this.markInvalid(String.format(this.minText, this.minValue));
43817 if(num > this.maxValue){
43818 this.markInvalid(String.format(this.maxText, this.maxValue));
43825 validate : function()
43827 if(this.disabled || this.allowBlank){
43832 var currency = this.getCurrency();
43834 if(this.validateValue(this.getRawValue()) && currency.length){
43839 this.markInvalid();
43843 getName: function()
43848 beforeBlur : function()
43854 var v = this.parseValue(this.getRawValue());
43861 onBlur : function()
43865 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43866 //this.el.removeClass(this.focusClass);
43869 this.hasFocus = false;
43871 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43875 var v = this.getValue();
43877 if(String(v) !== String(this.startValue)){
43878 this.fireEvent('change', this, v, this.startValue);
43881 this.fireEvent("blur", this);
43884 inputEl : function()
43886 return this.el.select('.roo-money-amount-input', true).first();
43889 currencyEl : function()
43891 return this.el.select('.roo-money-currency-input', true).first();
43894 hiddenEl : function()
43896 return this.el.select('input.hidden-number-input',true).first();
43900 * @class Roo.bootstrap.BezierSignature
43901 * @extends Roo.bootstrap.Component
43902 * Bootstrap BezierSignature class
43903 * This script refer to:
43904 * Title: Signature Pad
43906 * Availability: https://github.com/szimek/signature_pad
43909 * Create a new BezierSignature
43910 * @param {Object} config The config object
43913 Roo.bootstrap.BezierSignature = function(config){
43914 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43920 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43927 mouse_btn_down: true,
43930 * @cfg {int} canvas height
43932 canvas_height: '200px',
43935 * @cfg {float|function} Radius of a single dot.
43940 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43945 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43950 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43955 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43960 * @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.
43962 bg_color: 'rgba(0, 0, 0, 0)',
43965 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43967 dot_color: 'black',
43970 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43972 velocity_filter_weight: 0.7,
43975 * @cfg {function} Callback when stroke begin.
43980 * @cfg {function} Callback when stroke end.
43984 getAutoCreate : function()
43986 var cls = 'roo-signature column';
43989 cls += ' ' + this.cls;
43999 for(var i = 0; i < col_sizes.length; i++) {
44000 if(this[col_sizes[i]]) {
44001 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44011 cls: 'roo-signature-body',
44015 cls: 'roo-signature-body-canvas',
44016 height: this.canvas_height,
44017 width: this.canvas_width
44024 style: 'display: none'
44032 initEvents: function()
44034 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44036 var canvas = this.canvasEl();
44038 // mouse && touch event swapping...
44039 canvas.dom.style.touchAction = 'none';
44040 canvas.dom.style.msTouchAction = 'none';
44042 this.mouse_btn_down = false;
44043 canvas.on('mousedown', this._handleMouseDown, this);
44044 canvas.on('mousemove', this._handleMouseMove, this);
44045 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44047 if (window.PointerEvent) {
44048 canvas.on('pointerdown', this._handleMouseDown, this);
44049 canvas.on('pointermove', this._handleMouseMove, this);
44050 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44053 if ('ontouchstart' in window) {
44054 canvas.on('touchstart', this._handleTouchStart, this);
44055 canvas.on('touchmove', this._handleTouchMove, this);
44056 canvas.on('touchend', this._handleTouchEnd, this);
44059 Roo.EventManager.onWindowResize(this.resize, this, true);
44061 // file input event
44062 this.fileEl().on('change', this.uploadImage, this);
44069 resize: function(){
44071 var canvas = this.canvasEl().dom;
44072 var ctx = this.canvasElCtx();
44073 var img_data = false;
44075 if(canvas.width > 0) {
44076 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44078 // setting canvas width will clean img data
44081 var style = window.getComputedStyle ?
44082 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44084 var padding_left = parseInt(style.paddingLeft) || 0;
44085 var padding_right = parseInt(style.paddingRight) || 0;
44087 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44090 ctx.putImageData(img_data, 0, 0);
44094 _handleMouseDown: function(e)
44096 if (e.browserEvent.which === 1) {
44097 this.mouse_btn_down = true;
44098 this.strokeBegin(e);
44102 _handleMouseMove: function (e)
44104 if (this.mouse_btn_down) {
44105 this.strokeMoveUpdate(e);
44109 _handleMouseUp: function (e)
44111 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44112 this.mouse_btn_down = false;
44117 _handleTouchStart: function (e) {
44119 e.preventDefault();
44120 if (e.browserEvent.targetTouches.length === 1) {
44121 // var touch = e.browserEvent.changedTouches[0];
44122 // this.strokeBegin(touch);
44124 this.strokeBegin(e); // assume e catching the correct xy...
44128 _handleTouchMove: function (e) {
44129 e.preventDefault();
44130 // var touch = event.targetTouches[0];
44131 // _this._strokeMoveUpdate(touch);
44132 this.strokeMoveUpdate(e);
44135 _handleTouchEnd: function (e) {
44136 var wasCanvasTouched = e.target === this.canvasEl().dom;
44137 if (wasCanvasTouched) {
44138 e.preventDefault();
44139 // var touch = event.changedTouches[0];
44140 // _this._strokeEnd(touch);
44145 reset: function () {
44146 this._lastPoints = [];
44147 this._lastVelocity = 0;
44148 this._lastWidth = (this.min_width + this.max_width) / 2;
44149 this.canvasElCtx().fillStyle = this.dot_color;
44152 strokeMoveUpdate: function(e)
44154 this.strokeUpdate(e);
44156 if (this.throttle) {
44157 this.throttleStroke(this.strokeUpdate, this.throttle);
44160 this.strokeUpdate(e);
44164 strokeBegin: function(e)
44166 var newPointGroup = {
44167 color: this.dot_color,
44171 if (typeof this.onBegin === 'function') {
44175 this.curve_data.push(newPointGroup);
44177 this.strokeUpdate(e);
44180 strokeUpdate: function(e)
44182 var rect = this.canvasEl().dom.getBoundingClientRect();
44183 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44184 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44185 var lastPoints = lastPointGroup.points;
44186 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44187 var isLastPointTooClose = lastPoint
44188 ? point.distanceTo(lastPoint) <= this.min_distance
44190 var color = lastPointGroup.color;
44191 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44192 var curve = this.addPoint(point);
44194 this.drawDot({color: color, point: point});
44197 this.drawCurve({color: color, curve: curve});
44207 strokeEnd: function(e)
44209 this.strokeUpdate(e);
44210 if (typeof this.onEnd === 'function') {
44215 addPoint: function (point) {
44216 var _lastPoints = this._lastPoints;
44217 _lastPoints.push(point);
44218 if (_lastPoints.length > 2) {
44219 if (_lastPoints.length === 3) {
44220 _lastPoints.unshift(_lastPoints[0]);
44222 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44223 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44224 _lastPoints.shift();
44230 calculateCurveWidths: function (startPoint, endPoint) {
44231 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44232 (1 - this.velocity_filter_weight) * this._lastVelocity;
44234 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44237 start: this._lastWidth
44240 this._lastVelocity = velocity;
44241 this._lastWidth = newWidth;
44245 drawDot: function (_a) {
44246 var color = _a.color, point = _a.point;
44247 var ctx = this.canvasElCtx();
44248 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44250 this.drawCurveSegment(point.x, point.y, width);
44252 ctx.fillStyle = color;
44256 drawCurve: function (_a) {
44257 var color = _a.color, curve = _a.curve;
44258 var ctx = this.canvasElCtx();
44259 var widthDelta = curve.endWidth - curve.startWidth;
44260 var drawSteps = Math.floor(curve.length()) * 2;
44262 ctx.fillStyle = color;
44263 for (var i = 0; i < drawSteps; i += 1) {
44264 var t = i / drawSteps;
44270 var x = uuu * curve.startPoint.x;
44271 x += 3 * uu * t * curve.control1.x;
44272 x += 3 * u * tt * curve.control2.x;
44273 x += ttt * curve.endPoint.x;
44274 var y = uuu * curve.startPoint.y;
44275 y += 3 * uu * t * curve.control1.y;
44276 y += 3 * u * tt * curve.control2.y;
44277 y += ttt * curve.endPoint.y;
44278 var width = curve.startWidth + ttt * widthDelta;
44279 this.drawCurveSegment(x, y, width);
44285 drawCurveSegment: function (x, y, width) {
44286 var ctx = this.canvasElCtx();
44288 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44289 this.is_empty = false;
44294 var ctx = this.canvasElCtx();
44295 var canvas = this.canvasEl().dom;
44296 ctx.fillStyle = this.bg_color;
44297 ctx.clearRect(0, 0, canvas.width, canvas.height);
44298 ctx.fillRect(0, 0, canvas.width, canvas.height);
44299 this.curve_data = [];
44301 this.is_empty = true;
44306 return this.el.select('input',true).first();
44309 canvasEl: function()
44311 return this.el.select('canvas',true).first();
44314 canvasElCtx: function()
44316 return this.el.select('canvas',true).first().dom.getContext('2d');
44319 getImage: function(type)
44321 if(this.is_empty) {
44326 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44329 drawFromImage: function(img_src)
44331 var img = new Image();
44333 img.onload = function(){
44334 this.canvasElCtx().drawImage(img, 0, 0);
44339 this.is_empty = false;
44342 selectImage: function()
44344 this.fileEl().dom.click();
44347 uploadImage: function(e)
44349 var reader = new FileReader();
44351 reader.onload = function(e){
44352 var img = new Image();
44353 img.onload = function(){
44355 this.canvasElCtx().drawImage(img, 0, 0);
44357 img.src = e.target.result;
44360 reader.readAsDataURL(e.target.files[0]);
44363 // Bezier Point Constructor
44364 Point: (function () {
44365 function Point(x, y, time) {
44368 this.time = time || Date.now();
44370 Point.prototype.distanceTo = function (start) {
44371 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44373 Point.prototype.equals = function (other) {
44374 return this.x === other.x && this.y === other.y && this.time === other.time;
44376 Point.prototype.velocityFrom = function (start) {
44377 return this.time !== start.time
44378 ? this.distanceTo(start) / (this.time - start.time)
44385 // Bezier Constructor
44386 Bezier: (function () {
44387 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44388 this.startPoint = startPoint;
44389 this.control2 = control2;
44390 this.control1 = control1;
44391 this.endPoint = endPoint;
44392 this.startWidth = startWidth;
44393 this.endWidth = endWidth;
44395 Bezier.fromPoints = function (points, widths, scope) {
44396 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44397 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44398 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44400 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44401 var dx1 = s1.x - s2.x;
44402 var dy1 = s1.y - s2.y;
44403 var dx2 = s2.x - s3.x;
44404 var dy2 = s2.y - s3.y;
44405 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44406 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44407 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44408 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44409 var dxm = m1.x - m2.x;
44410 var dym = m1.y - m2.y;
44411 var k = l2 / (l1 + l2);
44412 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44413 var tx = s2.x - cm.x;
44414 var ty = s2.y - cm.y;
44416 c1: new scope.Point(m1.x + tx, m1.y + ty),
44417 c2: new scope.Point(m2.x + tx, m2.y + ty)
44420 Bezier.prototype.length = function () {
44425 for (var i = 0; i <= steps; i += 1) {
44427 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44428 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44430 var xdiff = cx - px;
44431 var ydiff = cy - py;
44432 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44439 Bezier.prototype.point = function (t, start, c1, c2, end) {
44440 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44441 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44442 + (3.0 * c2 * (1.0 - t) * t * t)
44443 + (end * t * t * t);
44448 throttleStroke: function(fn, wait) {
44449 if (wait === void 0) { wait = 250; }
44451 var timeout = null;
44455 var later = function () {
44456 previous = Date.now();
44458 result = fn.apply(storedContext, storedArgs);
44460 storedContext = null;
44464 return function wrapper() {
44466 for (var _i = 0; _i < arguments.length; _i++) {
44467 args[_i] = arguments[_i];
44469 var now = Date.now();
44470 var remaining = wait - (now - previous);
44471 storedContext = this;
44473 if (remaining <= 0 || remaining > wait) {
44475 clearTimeout(timeout);
44479 result = fn.apply(storedContext, storedArgs);
44481 storedContext = null;
44485 else if (!timeout) {
44486 timeout = window.setTimeout(later, remaining);