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,
12665 mimetype : file.type,
12672 addCard : function (data)
12674 // hidden input element?
12675 // if the file is not an image...
12676 //then we need to use something other that and header_image
12681 xns : Roo.bootstrap,
12682 xtype : 'CardFooter',
12685 xns : Roo.bootstrap,
12691 xns : Roo.bootstrap,
12693 html : String.format("<small>{0}</small>", data.title),
12694 cls : 'col-11 text-left',
12699 click : function() {
12700 this.downloadCard(data.id)
12706 xns : Roo.bootstrap,
12714 click : function() {
12715 t.removeCard(data.id)
12727 var cn = this.addxtype(
12730 xns : Roo.bootstrap,
12733 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12734 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12735 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12740 initEvents : function() {
12741 Roo.bootstrap.Card.prototype.initEvents.call(this);
12742 this.imgEl = this.el.select('.card-img-top').first();
12744 this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12745 this.imgEl.set({ 'pointer' : 'cursor' });
12754 // dont' really need ot update items.
12755 // this.items.push(cn);
12756 this.fileCollection.add(cn);
12758 if (!data.srcfile) {
12759 this.updateInput();
12764 var reader = new FileReader();
12765 reader.addEventListener("load", function() {
12766 data.srcdata = reader.result;
12769 reader.readAsDataURL(data.srcfile);
12774 removeCard : function(id)
12777 var card = this.fileCollection.get(id);
12778 card.data.is_deleted = 1;
12779 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12780 //this.fileCollection.remove(card);
12781 //this.items = this.items.filter(function(e) { return e != card });
12782 // dont' really need ot update items.
12783 card.el.dom.parentNode.removeChild(card.el.dom);
12784 this.updateInput();
12790 this.fileCollection.each(function(card) {
12791 if (card.el.dom && card.el.dom.parentNode) {
12792 card.el.dom.parentNode.removeChild(card.el.dom);
12795 this.fileCollection.clear();
12796 this.updateInput();
12799 updateInput : function()
12802 this.fileCollection.each(function(e) {
12806 this.inputEl().dom.value = JSON.stringify(data);
12816 Roo.bootstrap.CardUploader.ID = -1;/*
12818 * Ext JS Library 1.1.1
12819 * Copyright(c) 2006-2007, Ext JS, LLC.
12821 * Originally Released Under LGPL - original licence link has changed is not relivant.
12824 * <script type="text/javascript">
12829 * @class Roo.data.SortTypes
12831 * Defines the default sorting (casting?) comparison functions used when sorting data.
12833 Roo.data.SortTypes = {
12835 * Default sort that does nothing
12836 * @param {Mixed} s The value being converted
12837 * @return {Mixed} The comparison value
12839 none : function(s){
12844 * The regular expression used to strip tags
12848 stripTagsRE : /<\/?[^>]+>/gi,
12851 * Strips all HTML tags to sort on text only
12852 * @param {Mixed} s The value being converted
12853 * @return {String} The comparison value
12855 asText : function(s){
12856 return String(s).replace(this.stripTagsRE, "");
12860 * Strips all HTML tags to sort on text only - Case insensitive
12861 * @param {Mixed} s The value being converted
12862 * @return {String} The comparison value
12864 asUCText : function(s){
12865 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12869 * Case insensitive string
12870 * @param {Mixed} s The value being converted
12871 * @return {String} The comparison value
12873 asUCString : function(s) {
12874 return String(s).toUpperCase();
12879 * @param {Mixed} s The value being converted
12880 * @return {Number} The comparison value
12882 asDate : function(s) {
12886 if(s instanceof Date){
12887 return s.getTime();
12889 return Date.parse(String(s));
12894 * @param {Mixed} s The value being converted
12895 * @return {Float} The comparison value
12897 asFloat : function(s) {
12898 var val = parseFloat(String(s).replace(/,/g, ""));
12907 * @param {Mixed} s The value being converted
12908 * @return {Number} The comparison value
12910 asInt : function(s) {
12911 var val = parseInt(String(s).replace(/,/g, ""));
12919 * Ext JS Library 1.1.1
12920 * Copyright(c) 2006-2007, Ext JS, LLC.
12922 * Originally Released Under LGPL - original licence link has changed is not relivant.
12925 * <script type="text/javascript">
12929 * @class Roo.data.Record
12930 * Instances of this class encapsulate both record <em>definition</em> information, and record
12931 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12932 * to access Records cached in an {@link Roo.data.Store} object.<br>
12934 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12935 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12938 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12940 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12941 * {@link #create}. The parameters are the same.
12942 * @param {Array} data An associative Array of data values keyed by the field name.
12943 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12944 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12945 * not specified an integer id is generated.
12947 Roo.data.Record = function(data, id){
12948 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12953 * Generate a constructor for a specific record layout.
12954 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12955 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12956 * Each field definition object may contain the following properties: <ul>
12957 * <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,
12958 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12959 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12960 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12961 * is being used, then this is a string containing the javascript expression to reference the data relative to
12962 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12963 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12964 * this may be omitted.</p></li>
12965 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12966 * <ul><li>auto (Default, implies no conversion)</li>
12971 * <li>date</li></ul></p></li>
12972 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12973 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12974 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12975 * by the Reader into an object that will be stored in the Record. It is passed the
12976 * following parameters:<ul>
12977 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12979 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12981 * <br>usage:<br><pre><code>
12982 var TopicRecord = Roo.data.Record.create(
12983 {name: 'title', mapping: 'topic_title'},
12984 {name: 'author', mapping: 'username'},
12985 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12986 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12987 {name: 'lastPoster', mapping: 'user2'},
12988 {name: 'excerpt', mapping: 'post_text'}
12991 var myNewRecord = new TopicRecord({
12992 title: 'Do my job please',
12995 lastPost: new Date(),
12996 lastPoster: 'Animal',
12997 excerpt: 'No way dude!'
12999 myStore.add(myNewRecord);
13004 Roo.data.Record.create = function(o){
13005 var f = function(){
13006 f.superclass.constructor.apply(this, arguments);
13008 Roo.extend(f, Roo.data.Record);
13009 var p = f.prototype;
13010 p.fields = new Roo.util.MixedCollection(false, function(field){
13013 for(var i = 0, len = o.length; i < len; i++){
13014 p.fields.add(new Roo.data.Field(o[i]));
13016 f.getField = function(name){
13017 return p.fields.get(name);
13022 Roo.data.Record.AUTO_ID = 1000;
13023 Roo.data.Record.EDIT = 'edit';
13024 Roo.data.Record.REJECT = 'reject';
13025 Roo.data.Record.COMMIT = 'commit';
13027 Roo.data.Record.prototype = {
13029 * Readonly flag - true if this record has been modified.
13038 join : function(store){
13039 this.store = store;
13043 * Set the named field to the specified value.
13044 * @param {String} name The name of the field to set.
13045 * @param {Object} value The value to set the field to.
13047 set : function(name, value){
13048 if(this.data[name] == value){
13052 if(!this.modified){
13053 this.modified = {};
13055 if(typeof this.modified[name] == 'undefined'){
13056 this.modified[name] = this.data[name];
13058 this.data[name] = value;
13059 if(!this.editing && this.store){
13060 this.store.afterEdit(this);
13065 * Get the value of the named field.
13066 * @param {String} name The name of the field to get the value of.
13067 * @return {Object} The value of the field.
13069 get : function(name){
13070 return this.data[name];
13074 beginEdit : function(){
13075 this.editing = true;
13076 this.modified = {};
13080 cancelEdit : function(){
13081 this.editing = false;
13082 delete this.modified;
13086 endEdit : function(){
13087 this.editing = false;
13088 if(this.dirty && this.store){
13089 this.store.afterEdit(this);
13094 * Usually called by the {@link Roo.data.Store} which owns the Record.
13095 * Rejects all changes made to the Record since either creation, or the last commit operation.
13096 * Modified fields are reverted to their original values.
13098 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13099 * of reject operations.
13101 reject : function(){
13102 var m = this.modified;
13104 if(typeof m[n] != "function"){
13105 this.data[n] = m[n];
13108 this.dirty = false;
13109 delete this.modified;
13110 this.editing = false;
13112 this.store.afterReject(this);
13117 * Usually called by the {@link Roo.data.Store} which owns the Record.
13118 * Commits all changes made to the Record since either creation, or the last commit operation.
13120 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13121 * of commit operations.
13123 commit : function(){
13124 this.dirty = false;
13125 delete this.modified;
13126 this.editing = false;
13128 this.store.afterCommit(this);
13133 hasError : function(){
13134 return this.error != null;
13138 clearError : function(){
13143 * Creates a copy of this record.
13144 * @param {String} id (optional) A new record id if you don't want to use this record's id
13147 copy : function(newId) {
13148 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13152 * Ext JS Library 1.1.1
13153 * Copyright(c) 2006-2007, Ext JS, LLC.
13155 * Originally Released Under LGPL - original licence link has changed is not relivant.
13158 * <script type="text/javascript">
13164 * @class Roo.data.Store
13165 * @extends Roo.util.Observable
13166 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13167 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13169 * 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
13170 * has no knowledge of the format of the data returned by the Proxy.<br>
13172 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13173 * instances from the data object. These records are cached and made available through accessor functions.
13175 * Creates a new Store.
13176 * @param {Object} config A config object containing the objects needed for the Store to access data,
13177 * and read the data into Records.
13179 Roo.data.Store = function(config){
13180 this.data = new Roo.util.MixedCollection(false);
13181 this.data.getKey = function(o){
13184 this.baseParams = {};
13186 this.paramNames = {
13191 "multisort" : "_multisort"
13194 if(config && config.data){
13195 this.inlineData = config.data;
13196 delete config.data;
13199 Roo.apply(this, config);
13201 if(this.reader){ // reader passed
13202 this.reader = Roo.factory(this.reader, Roo.data);
13203 this.reader.xmodule = this.xmodule || false;
13204 if(!this.recordType){
13205 this.recordType = this.reader.recordType;
13207 if(this.reader.onMetaChange){
13208 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13212 if(this.recordType){
13213 this.fields = this.recordType.prototype.fields;
13215 this.modified = [];
13219 * @event datachanged
13220 * Fires when the data cache has changed, and a widget which is using this Store
13221 * as a Record cache should refresh its view.
13222 * @param {Store} this
13224 datachanged : true,
13226 * @event metachange
13227 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13228 * @param {Store} this
13229 * @param {Object} meta The JSON metadata
13234 * Fires when Records have been added to the Store
13235 * @param {Store} this
13236 * @param {Roo.data.Record[]} records The array of Records added
13237 * @param {Number} index The index at which the record(s) were added
13242 * Fires when a Record has been removed from the Store
13243 * @param {Store} this
13244 * @param {Roo.data.Record} record The Record that was removed
13245 * @param {Number} index The index at which the record was removed
13250 * Fires when a Record has been updated
13251 * @param {Store} this
13252 * @param {Roo.data.Record} record The Record that was updated
13253 * @param {String} operation The update operation being performed. Value may be one of:
13255 Roo.data.Record.EDIT
13256 Roo.data.Record.REJECT
13257 Roo.data.Record.COMMIT
13263 * Fires when the data cache has been cleared.
13264 * @param {Store} this
13268 * @event beforeload
13269 * Fires before a request is made for a new data object. If the beforeload handler returns false
13270 * the load action will be canceled.
13271 * @param {Store} this
13272 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13276 * @event beforeloadadd
13277 * Fires after a new set of Records has been loaded.
13278 * @param {Store} this
13279 * @param {Roo.data.Record[]} records The Records that were loaded
13280 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13282 beforeloadadd : true,
13285 * Fires after a new set of Records has been loaded, before they are added to the store.
13286 * @param {Store} this
13287 * @param {Roo.data.Record[]} records The Records that were loaded
13288 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13289 * @params {Object} return from reader
13293 * @event loadexception
13294 * Fires if an exception occurs in the Proxy during loading.
13295 * Called with the signature of the Proxy's "loadexception" event.
13296 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13299 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13300 * @param {Object} load options
13301 * @param {Object} jsonData from your request (normally this contains the Exception)
13303 loadexception : true
13307 this.proxy = Roo.factory(this.proxy, Roo.data);
13308 this.proxy.xmodule = this.xmodule || false;
13309 this.relayEvents(this.proxy, ["loadexception"]);
13311 this.sortToggle = {};
13312 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13314 Roo.data.Store.superclass.constructor.call(this);
13316 if(this.inlineData){
13317 this.loadData(this.inlineData);
13318 delete this.inlineData;
13322 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13324 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13325 * without a remote query - used by combo/forms at present.
13329 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13332 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13335 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13336 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13339 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13340 * on any HTTP request
13343 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13346 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13350 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13351 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13353 remoteSort : false,
13356 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13357 * loaded or when a record is removed. (defaults to false).
13359 pruneModifiedRecords : false,
13362 lastOptions : null,
13365 * Add Records to the Store and fires the add event.
13366 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13368 add : function(records){
13369 records = [].concat(records);
13370 for(var i = 0, len = records.length; i < len; i++){
13371 records[i].join(this);
13373 var index = this.data.length;
13374 this.data.addAll(records);
13375 this.fireEvent("add", this, records, index);
13379 * Remove a Record from the Store and fires the remove event.
13380 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13382 remove : function(record){
13383 var index = this.data.indexOf(record);
13384 this.data.removeAt(index);
13386 if(this.pruneModifiedRecords){
13387 this.modified.remove(record);
13389 this.fireEvent("remove", this, record, index);
13393 * Remove all Records from the Store and fires the clear event.
13395 removeAll : function(){
13397 if(this.pruneModifiedRecords){
13398 this.modified = [];
13400 this.fireEvent("clear", this);
13404 * Inserts Records to the Store at the given index and fires the add event.
13405 * @param {Number} index The start index at which to insert the passed Records.
13406 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13408 insert : function(index, records){
13409 records = [].concat(records);
13410 for(var i = 0, len = records.length; i < len; i++){
13411 this.data.insert(index, records[i]);
13412 records[i].join(this);
13414 this.fireEvent("add", this, records, index);
13418 * Get the index within the cache of the passed Record.
13419 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13420 * @return {Number} The index of the passed Record. Returns -1 if not found.
13422 indexOf : function(record){
13423 return this.data.indexOf(record);
13427 * Get the index within the cache of the Record with the passed id.
13428 * @param {String} id The id of the Record to find.
13429 * @return {Number} The index of the Record. Returns -1 if not found.
13431 indexOfId : function(id){
13432 return this.data.indexOfKey(id);
13436 * Get the Record with the specified id.
13437 * @param {String} id The id of the Record to find.
13438 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13440 getById : function(id){
13441 return this.data.key(id);
13445 * Get the Record at the specified index.
13446 * @param {Number} index The index of the Record to find.
13447 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13449 getAt : function(index){
13450 return this.data.itemAt(index);
13454 * Returns a range of Records between specified indices.
13455 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13456 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13457 * @return {Roo.data.Record[]} An array of Records
13459 getRange : function(start, end){
13460 return this.data.getRange(start, end);
13464 storeOptions : function(o){
13465 o = Roo.apply({}, o);
13468 this.lastOptions = o;
13472 * Loads the Record cache from the configured Proxy using the configured Reader.
13474 * If using remote paging, then the first load call must specify the <em>start</em>
13475 * and <em>limit</em> properties in the options.params property to establish the initial
13476 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13478 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13479 * and this call will return before the new data has been loaded. Perform any post-processing
13480 * in a callback function, or in a "load" event handler.</strong>
13482 * @param {Object} options An object containing properties which control loading options:<ul>
13483 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13484 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13485 * passed the following arguments:<ul>
13486 * <li>r : Roo.data.Record[]</li>
13487 * <li>options: Options object from the load call</li>
13488 * <li>success: Boolean success indicator</li></ul></li>
13489 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13490 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13493 load : function(options){
13494 options = options || {};
13495 if(this.fireEvent("beforeload", this, options) !== false){
13496 this.storeOptions(options);
13497 var p = Roo.apply(options.params || {}, this.baseParams);
13498 // if meta was not loaded from remote source.. try requesting it.
13499 if (!this.reader.metaFromRemote) {
13500 p._requestMeta = 1;
13502 if(this.sortInfo && this.remoteSort){
13503 var pn = this.paramNames;
13504 p[pn["sort"]] = this.sortInfo.field;
13505 p[pn["dir"]] = this.sortInfo.direction;
13507 if (this.multiSort) {
13508 var pn = this.paramNames;
13509 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13512 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13517 * Reloads the Record cache from the configured Proxy using the configured Reader and
13518 * the options from the last load operation performed.
13519 * @param {Object} options (optional) An object containing properties which may override the options
13520 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13521 * the most recently used options are reused).
13523 reload : function(options){
13524 this.load(Roo.applyIf(options||{}, this.lastOptions));
13528 // Called as a callback by the Reader during a load operation.
13529 loadRecords : function(o, options, success){
13530 if(!o || success === false){
13531 if(success !== false){
13532 this.fireEvent("load", this, [], options, o);
13534 if(options.callback){
13535 options.callback.call(options.scope || this, [], options, false);
13539 // if data returned failure - throw an exception.
13540 if (o.success === false) {
13541 // show a message if no listener is registered.
13542 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13543 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13545 // loadmask wil be hooked into this..
13546 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13549 var r = o.records, t = o.totalRecords || r.length;
13551 this.fireEvent("beforeloadadd", this, r, options, o);
13553 if(!options || options.add !== true){
13554 if(this.pruneModifiedRecords){
13555 this.modified = [];
13557 for(var i = 0, len = r.length; i < len; i++){
13561 this.data = this.snapshot;
13562 delete this.snapshot;
13565 this.data.addAll(r);
13566 this.totalLength = t;
13568 this.fireEvent("datachanged", this);
13570 this.totalLength = Math.max(t, this.data.length+r.length);
13574 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13576 var e = new Roo.data.Record({});
13578 e.set(this.parent.displayField, this.parent.emptyTitle);
13579 e.set(this.parent.valueField, '');
13584 this.fireEvent("load", this, r, options, o);
13585 if(options.callback){
13586 options.callback.call(options.scope || this, r, options, true);
13592 * Loads data from a passed data block. A Reader which understands the format of the data
13593 * must have been configured in the constructor.
13594 * @param {Object} data The data block from which to read the Records. The format of the data expected
13595 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13596 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13598 loadData : function(o, append){
13599 var r = this.reader.readRecords(o);
13600 this.loadRecords(r, {add: append}, true);
13604 * using 'cn' the nested child reader read the child array into it's child stores.
13605 * @param {Object} rec The record with a 'children array
13607 loadDataFromChildren : function(rec)
13609 this.loadData(this.reader.toLoadData(rec));
13614 * Gets the number of cached records.
13616 * <em>If using paging, this may not be the total size of the dataset. If the data object
13617 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13618 * the data set size</em>
13620 getCount : function(){
13621 return this.data.length || 0;
13625 * Gets the total number of records in the dataset as returned by the server.
13627 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13628 * the dataset size</em>
13630 getTotalCount : function(){
13631 return this.totalLength || 0;
13635 * Returns the sort state of the Store as an object with two properties:
13637 field {String} The name of the field by which the Records are sorted
13638 direction {String} The sort order, "ASC" or "DESC"
13641 getSortState : function(){
13642 return this.sortInfo;
13646 applySort : function(){
13647 if(this.sortInfo && !this.remoteSort){
13648 var s = this.sortInfo, f = s.field;
13649 var st = this.fields.get(f).sortType;
13650 var fn = function(r1, r2){
13651 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13652 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13654 this.data.sort(s.direction, fn);
13655 if(this.snapshot && this.snapshot != this.data){
13656 this.snapshot.sort(s.direction, fn);
13662 * Sets the default sort column and order to be used by the next load operation.
13663 * @param {String} fieldName The name of the field to sort by.
13664 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13666 setDefaultSort : function(field, dir){
13667 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13671 * Sort the Records.
13672 * If remote sorting is used, the sort is performed on the server, and the cache is
13673 * reloaded. If local sorting is used, the cache is sorted internally.
13674 * @param {String} fieldName The name of the field to sort by.
13675 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13677 sort : function(fieldName, dir){
13678 var f = this.fields.get(fieldName);
13680 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13682 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13683 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13688 this.sortToggle[f.name] = dir;
13689 this.sortInfo = {field: f.name, direction: dir};
13690 if(!this.remoteSort){
13692 this.fireEvent("datachanged", this);
13694 this.load(this.lastOptions);
13699 * Calls the specified function for each of the Records in the cache.
13700 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13701 * Returning <em>false</em> aborts and exits the iteration.
13702 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13704 each : function(fn, scope){
13705 this.data.each(fn, scope);
13709 * Gets all records modified since the last commit. Modified records are persisted across load operations
13710 * (e.g., during paging).
13711 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13713 getModifiedRecords : function(){
13714 return this.modified;
13718 createFilterFn : function(property, value, anyMatch){
13719 if(!value.exec){ // not a regex
13720 value = String(value);
13721 if(value.length == 0){
13724 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13726 return function(r){
13727 return value.test(r.data[property]);
13732 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13733 * @param {String} property A field on your records
13734 * @param {Number} start The record index to start at (defaults to 0)
13735 * @param {Number} end The last record index to include (defaults to length - 1)
13736 * @return {Number} The sum
13738 sum : function(property, start, end){
13739 var rs = this.data.items, v = 0;
13740 start = start || 0;
13741 end = (end || end === 0) ? end : rs.length-1;
13743 for(var i = start; i <= end; i++){
13744 v += (rs[i].data[property] || 0);
13750 * Filter the records by a specified property.
13751 * @param {String} field A field on your records
13752 * @param {String/RegExp} value Either a string that the field
13753 * should start with or a RegExp to test against the field
13754 * @param {Boolean} anyMatch True to match any part not just the beginning
13756 filter : function(property, value, anyMatch){
13757 var fn = this.createFilterFn(property, value, anyMatch);
13758 return fn ? this.filterBy(fn) : this.clearFilter();
13762 * Filter by a function. The specified function will be called with each
13763 * record in this data source. If the function returns true the record is included,
13764 * otherwise it is filtered.
13765 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13766 * @param {Object} scope (optional) The scope of the function (defaults to this)
13768 filterBy : function(fn, scope){
13769 this.snapshot = this.snapshot || this.data;
13770 this.data = this.queryBy(fn, scope||this);
13771 this.fireEvent("datachanged", this);
13775 * Query the records by a specified property.
13776 * @param {String} field A field on your records
13777 * @param {String/RegExp} value Either a string that the field
13778 * should start with or a RegExp to test against the field
13779 * @param {Boolean} anyMatch True to match any part not just the beginning
13780 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13782 query : function(property, value, anyMatch){
13783 var fn = this.createFilterFn(property, value, anyMatch);
13784 return fn ? this.queryBy(fn) : this.data.clone();
13788 * Query by a function. The specified function will be called with each
13789 * record in this data source. If the function returns true the record is included
13791 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13792 * @param {Object} scope (optional) The scope of the function (defaults to this)
13793 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13795 queryBy : function(fn, scope){
13796 var data = this.snapshot || this.data;
13797 return data.filterBy(fn, scope||this);
13801 * Collects unique values for a particular dataIndex from this store.
13802 * @param {String} dataIndex The property to collect
13803 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13804 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13805 * @return {Array} An array of the unique values
13807 collect : function(dataIndex, allowNull, bypassFilter){
13808 var d = (bypassFilter === true && this.snapshot) ?
13809 this.snapshot.items : this.data.items;
13810 var v, sv, r = [], l = {};
13811 for(var i = 0, len = d.length; i < len; i++){
13812 v = d[i].data[dataIndex];
13814 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13823 * Revert to a view of the Record cache with no filtering applied.
13824 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13826 clearFilter : function(suppressEvent){
13827 if(this.snapshot && this.snapshot != this.data){
13828 this.data = this.snapshot;
13829 delete this.snapshot;
13830 if(suppressEvent !== true){
13831 this.fireEvent("datachanged", this);
13837 afterEdit : function(record){
13838 if(this.modified.indexOf(record) == -1){
13839 this.modified.push(record);
13841 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13845 afterReject : function(record){
13846 this.modified.remove(record);
13847 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13851 afterCommit : function(record){
13852 this.modified.remove(record);
13853 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13857 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13858 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13860 commitChanges : function(){
13861 var m = this.modified.slice(0);
13862 this.modified = [];
13863 for(var i = 0, len = m.length; i < len; i++){
13869 * Cancel outstanding changes on all changed records.
13871 rejectChanges : function(){
13872 var m = this.modified.slice(0);
13873 this.modified = [];
13874 for(var i = 0, len = m.length; i < len; i++){
13879 onMetaChange : function(meta, rtype, o){
13880 this.recordType = rtype;
13881 this.fields = rtype.prototype.fields;
13882 delete this.snapshot;
13883 this.sortInfo = meta.sortInfo || this.sortInfo;
13884 this.modified = [];
13885 this.fireEvent('metachange', this, this.reader.meta);
13888 moveIndex : function(data, type)
13890 var index = this.indexOf(data);
13892 var newIndex = index + type;
13896 this.insert(newIndex, data);
13901 * Ext JS Library 1.1.1
13902 * Copyright(c) 2006-2007, Ext JS, LLC.
13904 * Originally Released Under LGPL - original licence link has changed is not relivant.
13907 * <script type="text/javascript">
13911 * @class Roo.data.SimpleStore
13912 * @extends Roo.data.Store
13913 * Small helper class to make creating Stores from Array data easier.
13914 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13915 * @cfg {Array} fields An array of field definition objects, or field name strings.
13916 * @cfg {Object} an existing reader (eg. copied from another store)
13917 * @cfg {Array} data The multi-dimensional array of data
13919 * @param {Object} config
13921 Roo.data.SimpleStore = function(config)
13923 Roo.data.SimpleStore.superclass.constructor.call(this, {
13925 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13928 Roo.data.Record.create(config.fields)
13930 proxy : new Roo.data.MemoryProxy(config.data)
13934 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13936 * Ext JS Library 1.1.1
13937 * Copyright(c) 2006-2007, Ext JS, LLC.
13939 * Originally Released Under LGPL - original licence link has changed is not relivant.
13942 * <script type="text/javascript">
13947 * @extends Roo.data.Store
13948 * @class Roo.data.JsonStore
13949 * Small helper class to make creating Stores for JSON data easier. <br/>
13951 var store = new Roo.data.JsonStore({
13952 url: 'get-images.php',
13954 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13957 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13958 * JsonReader and HttpProxy (unless inline data is provided).</b>
13959 * @cfg {Array} fields An array of field definition objects, or field name strings.
13961 * @param {Object} config
13963 Roo.data.JsonStore = function(c){
13964 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13965 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13966 reader: new Roo.data.JsonReader(c, c.fields)
13969 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13971 * Ext JS Library 1.1.1
13972 * Copyright(c) 2006-2007, Ext JS, LLC.
13974 * Originally Released Under LGPL - original licence link has changed is not relivant.
13977 * <script type="text/javascript">
13981 Roo.data.Field = function(config){
13982 if(typeof config == "string"){
13983 config = {name: config};
13985 Roo.apply(this, config);
13988 this.type = "auto";
13991 var st = Roo.data.SortTypes;
13992 // named sortTypes are supported, here we look them up
13993 if(typeof this.sortType == "string"){
13994 this.sortType = st[this.sortType];
13997 // set default sortType for strings and dates
13998 if(!this.sortType){
14001 this.sortType = st.asUCString;
14004 this.sortType = st.asDate;
14007 this.sortType = st.none;
14012 var stripRe = /[\$,%]/g;
14014 // prebuilt conversion function for this field, instead of
14015 // switching every time we're reading a value
14017 var cv, dateFormat = this.dateFormat;
14022 cv = function(v){ return v; };
14025 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14029 return v !== undefined && v !== null && v !== '' ?
14030 parseInt(String(v).replace(stripRe, ""), 10) : '';
14035 return v !== undefined && v !== null && v !== '' ?
14036 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14041 cv = function(v){ return v === true || v === "true" || v == 1; };
14048 if(v instanceof Date){
14052 if(dateFormat == "timestamp"){
14053 return new Date(v*1000);
14055 return Date.parseDate(v, dateFormat);
14057 var parsed = Date.parse(v);
14058 return parsed ? new Date(parsed) : null;
14067 Roo.data.Field.prototype = {
14075 * Ext JS Library 1.1.1
14076 * Copyright(c) 2006-2007, Ext JS, LLC.
14078 * Originally Released Under LGPL - original licence link has changed is not relivant.
14081 * <script type="text/javascript">
14084 // Base class for reading structured data from a data source. This class is intended to be
14085 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14088 * @class Roo.data.DataReader
14089 * Base class for reading structured data from a data source. This class is intended to be
14090 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14093 Roo.data.DataReader = function(meta, recordType){
14097 this.recordType = recordType instanceof Array ?
14098 Roo.data.Record.create(recordType) : recordType;
14101 Roo.data.DataReader.prototype = {
14104 readerType : 'Data',
14106 * Create an empty record
14107 * @param {Object} data (optional) - overlay some values
14108 * @return {Roo.data.Record} record created.
14110 newRow : function(d) {
14112 this.recordType.prototype.fields.each(function(c) {
14114 case 'int' : da[c.name] = 0; break;
14115 case 'date' : da[c.name] = new Date(); break;
14116 case 'float' : da[c.name] = 0.0; break;
14117 case 'boolean' : da[c.name] = false; break;
14118 default : da[c.name] = ""; break;
14122 return new this.recordType(Roo.apply(da, d));
14128 * Ext JS Library 1.1.1
14129 * Copyright(c) 2006-2007, Ext JS, LLC.
14131 * Originally Released Under LGPL - original licence link has changed is not relivant.
14134 * <script type="text/javascript">
14138 * @class Roo.data.DataProxy
14139 * @extends Roo.data.Observable
14140 * This class is an abstract base class for implementations which provide retrieval of
14141 * unformatted data objects.<br>
14143 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14144 * (of the appropriate type which knows how to parse the data object) to provide a block of
14145 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14147 * Custom implementations must implement the load method as described in
14148 * {@link Roo.data.HttpProxy#load}.
14150 Roo.data.DataProxy = function(){
14153 * @event beforeload
14154 * Fires before a network request is made to retrieve a data object.
14155 * @param {Object} This DataProxy object.
14156 * @param {Object} params The params parameter to the load function.
14161 * Fires before the load method's callback is called.
14162 * @param {Object} This DataProxy object.
14163 * @param {Object} o The data object.
14164 * @param {Object} arg The callback argument object passed to the load function.
14168 * @event loadexception
14169 * Fires if an Exception occurs during data retrieval.
14170 * @param {Object} This DataProxy object.
14171 * @param {Object} o The data object.
14172 * @param {Object} arg The callback argument object passed to the load function.
14173 * @param {Object} e The Exception.
14175 loadexception : true
14177 Roo.data.DataProxy.superclass.constructor.call(this);
14180 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14183 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14187 * Ext JS Library 1.1.1
14188 * Copyright(c) 2006-2007, Ext JS, LLC.
14190 * Originally Released Under LGPL - original licence link has changed is not relivant.
14193 * <script type="text/javascript">
14196 * @class Roo.data.MemoryProxy
14197 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14198 * to the Reader when its load method is called.
14200 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14202 Roo.data.MemoryProxy = function(data){
14206 Roo.data.MemoryProxy.superclass.constructor.call(this);
14210 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14213 * Load data from the requested source (in this case an in-memory
14214 * data object passed to the constructor), read the data object into
14215 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14216 * process that block using the passed callback.
14217 * @param {Object} params This parameter is not used by the MemoryProxy class.
14218 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14219 * object into a block of Roo.data.Records.
14220 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14221 * The function must be passed <ul>
14222 * <li>The Record block object</li>
14223 * <li>The "arg" argument from the load function</li>
14224 * <li>A boolean success indicator</li>
14226 * @param {Object} scope The scope in which to call the callback
14227 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14229 load : function(params, reader, callback, scope, arg){
14230 params = params || {};
14233 result = reader.readRecords(params.data ? params.data :this.data);
14235 this.fireEvent("loadexception", this, arg, null, e);
14236 callback.call(scope, null, arg, false);
14239 callback.call(scope, result, arg, true);
14243 update : function(params, records){
14248 * Ext JS Library 1.1.1
14249 * Copyright(c) 2006-2007, Ext JS, LLC.
14251 * Originally Released Under LGPL - original licence link has changed is not relivant.
14254 * <script type="text/javascript">
14257 * @class Roo.data.HttpProxy
14258 * @extends Roo.data.DataProxy
14259 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14260 * configured to reference a certain URL.<br><br>
14262 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14263 * from which the running page was served.<br><br>
14265 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14267 * Be aware that to enable the browser to parse an XML document, the server must set
14268 * the Content-Type header in the HTTP response to "text/xml".
14270 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14271 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14272 * will be used to make the request.
14274 Roo.data.HttpProxy = function(conn){
14275 Roo.data.HttpProxy.superclass.constructor.call(this);
14276 // is conn a conn config or a real conn?
14278 this.useAjax = !conn || !conn.events;
14282 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14283 // thse are take from connection...
14286 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14289 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14290 * extra parameters to each request made by this object. (defaults to undefined)
14293 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14294 * to each request made by this object. (defaults to undefined)
14297 * @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)
14300 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14303 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14309 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14313 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14314 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14315 * a finer-grained basis than the DataProxy events.
14317 getConnection : function(){
14318 return this.useAjax ? Roo.Ajax : this.conn;
14322 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14323 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14324 * process that block using the passed callback.
14325 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14326 * for the request to the remote server.
14327 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14328 * object into a block of Roo.data.Records.
14329 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14330 * The function must be passed <ul>
14331 * <li>The Record block object</li>
14332 * <li>The "arg" argument from the load function</li>
14333 * <li>A boolean success indicator</li>
14335 * @param {Object} scope The scope in which to call the callback
14336 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14338 load : function(params, reader, callback, scope, arg){
14339 if(this.fireEvent("beforeload", this, params) !== false){
14341 params : params || {},
14343 callback : callback,
14348 callback : this.loadResponse,
14352 Roo.applyIf(o, this.conn);
14353 if(this.activeRequest){
14354 Roo.Ajax.abort(this.activeRequest);
14356 this.activeRequest = Roo.Ajax.request(o);
14358 this.conn.request(o);
14361 callback.call(scope||this, null, arg, false);
14366 loadResponse : function(o, success, response){
14367 delete this.activeRequest;
14369 this.fireEvent("loadexception", this, o, response);
14370 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14375 result = o.reader.read(response);
14377 this.fireEvent("loadexception", this, o, response, e);
14378 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14382 this.fireEvent("load", this, o, o.request.arg);
14383 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14387 update : function(dataSet){
14392 updateResponse : function(dataSet){
14397 * Ext JS Library 1.1.1
14398 * Copyright(c) 2006-2007, Ext JS, LLC.
14400 * Originally Released Under LGPL - original licence link has changed is not relivant.
14403 * <script type="text/javascript">
14407 * @class Roo.data.ScriptTagProxy
14408 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14409 * other than the originating domain of the running page.<br><br>
14411 * <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
14412 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14414 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14415 * source code that is used as the source inside a <script> tag.<br><br>
14417 * In order for the browser to process the returned data, the server must wrap the data object
14418 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14419 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14420 * depending on whether the callback name was passed:
14423 boolean scriptTag = false;
14424 String cb = request.getParameter("callback");
14427 response.setContentType("text/javascript");
14429 response.setContentType("application/x-json");
14431 Writer out = response.getWriter();
14433 out.write(cb + "(");
14435 out.print(dataBlock.toJsonString());
14442 * @param {Object} config A configuration object.
14444 Roo.data.ScriptTagProxy = function(config){
14445 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14446 Roo.apply(this, config);
14447 this.head = document.getElementsByTagName("head")[0];
14450 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14452 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14454 * @cfg {String} url The URL from which to request the data object.
14457 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14461 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14462 * the server the name of the callback function set up by the load call to process the returned data object.
14463 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14464 * javascript output which calls this named function passing the data object as its only parameter.
14466 callbackParam : "callback",
14468 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14469 * name to the request.
14474 * Load data from the configured URL, read the data object into
14475 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14476 * process that block using the passed callback.
14477 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14478 * for the request to the remote server.
14479 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14480 * object into a block of Roo.data.Records.
14481 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14482 * The function must be passed <ul>
14483 * <li>The Record block object</li>
14484 * <li>The "arg" argument from the load function</li>
14485 * <li>A boolean success indicator</li>
14487 * @param {Object} scope The scope in which to call the callback
14488 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14490 load : function(params, reader, callback, scope, arg){
14491 if(this.fireEvent("beforeload", this, params) !== false){
14493 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14495 var url = this.url;
14496 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14498 url += "&_dc=" + (new Date().getTime());
14500 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14503 cb : "stcCallback"+transId,
14504 scriptId : "stcScript"+transId,
14508 callback : callback,
14514 window[trans.cb] = function(o){
14515 conn.handleResponse(o, trans);
14518 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14520 if(this.autoAbort !== false){
14524 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14526 var script = document.createElement("script");
14527 script.setAttribute("src", url);
14528 script.setAttribute("type", "text/javascript");
14529 script.setAttribute("id", trans.scriptId);
14530 this.head.appendChild(script);
14532 this.trans = trans;
14534 callback.call(scope||this, null, arg, false);
14539 isLoading : function(){
14540 return this.trans ? true : false;
14544 * Abort the current server request.
14546 abort : function(){
14547 if(this.isLoading()){
14548 this.destroyTrans(this.trans);
14553 destroyTrans : function(trans, isLoaded){
14554 this.head.removeChild(document.getElementById(trans.scriptId));
14555 clearTimeout(trans.timeoutId);
14557 window[trans.cb] = undefined;
14559 delete window[trans.cb];
14562 // if hasn't been loaded, wait for load to remove it to prevent script error
14563 window[trans.cb] = function(){
14564 window[trans.cb] = undefined;
14566 delete window[trans.cb];
14573 handleResponse : function(o, trans){
14574 this.trans = false;
14575 this.destroyTrans(trans, true);
14578 result = trans.reader.readRecords(o);
14580 this.fireEvent("loadexception", this, o, trans.arg, e);
14581 trans.callback.call(trans.scope||window, null, trans.arg, false);
14584 this.fireEvent("load", this, o, trans.arg);
14585 trans.callback.call(trans.scope||window, result, trans.arg, true);
14589 handleFailure : function(trans){
14590 this.trans = false;
14591 this.destroyTrans(trans, false);
14592 this.fireEvent("loadexception", this, null, trans.arg);
14593 trans.callback.call(trans.scope||window, null, trans.arg, false);
14597 * Ext JS Library 1.1.1
14598 * Copyright(c) 2006-2007, Ext JS, LLC.
14600 * Originally Released Under LGPL - original licence link has changed is not relivant.
14603 * <script type="text/javascript">
14607 * @class Roo.data.JsonReader
14608 * @extends Roo.data.DataReader
14609 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14610 * based on mappings in a provided Roo.data.Record constructor.
14612 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14613 * in the reply previously.
14618 var RecordDef = Roo.data.Record.create([
14619 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14620 {name: 'occupation'} // This field will use "occupation" as the mapping.
14622 var myReader = new Roo.data.JsonReader({
14623 totalProperty: "results", // The property which contains the total dataset size (optional)
14624 root: "rows", // The property which contains an Array of row objects
14625 id: "id" // The property within each row object that provides an ID for the record (optional)
14629 * This would consume a JSON file like this:
14631 { 'results': 2, 'rows': [
14632 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14633 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14636 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14637 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14638 * paged from the remote server.
14639 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14640 * @cfg {String} root name of the property which contains the Array of row objects.
14641 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14642 * @cfg {Array} fields Array of field definition objects
14644 * Create a new JsonReader
14645 * @param {Object} meta Metadata configuration options
14646 * @param {Object} recordType Either an Array of field definition objects,
14647 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14649 Roo.data.JsonReader = function(meta, recordType){
14652 // set some defaults:
14653 Roo.applyIf(meta, {
14654 totalProperty: 'total',
14655 successProperty : 'success',
14660 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14662 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14664 readerType : 'Json',
14667 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14668 * Used by Store query builder to append _requestMeta to params.
14671 metaFromRemote : false,
14673 * This method is only used by a DataProxy which has retrieved data from a remote server.
14674 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14675 * @return {Object} data A data block which is used by an Roo.data.Store object as
14676 * a cache of Roo.data.Records.
14678 read : function(response){
14679 var json = response.responseText;
14681 var o = /* eval:var:o */ eval("("+json+")");
14683 throw {message: "JsonReader.read: Json object not found"};
14689 this.metaFromRemote = true;
14690 this.meta = o.metaData;
14691 this.recordType = Roo.data.Record.create(o.metaData.fields);
14692 this.onMetaChange(this.meta, this.recordType, o);
14694 return this.readRecords(o);
14697 // private function a store will implement
14698 onMetaChange : function(meta, recordType, o){
14705 simpleAccess: function(obj, subsc) {
14712 getJsonAccessor: function(){
14714 return function(expr) {
14716 return(re.test(expr))
14717 ? new Function("obj", "return obj." + expr)
14722 return Roo.emptyFn;
14727 * Create a data block containing Roo.data.Records from an XML document.
14728 * @param {Object} o An object which contains an Array of row objects in the property specified
14729 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14730 * which contains the total size of the dataset.
14731 * @return {Object} data A data block which is used by an Roo.data.Store object as
14732 * a cache of Roo.data.Records.
14734 readRecords : function(o){
14736 * After any data loads, the raw JSON data is available for further custom processing.
14740 var s = this.meta, Record = this.recordType,
14741 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14743 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14745 if(s.totalProperty) {
14746 this.getTotal = this.getJsonAccessor(s.totalProperty);
14748 if(s.successProperty) {
14749 this.getSuccess = this.getJsonAccessor(s.successProperty);
14751 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14753 var g = this.getJsonAccessor(s.id);
14754 this.getId = function(rec) {
14756 return (r === undefined || r === "") ? null : r;
14759 this.getId = function(){return null;};
14762 for(var jj = 0; jj < fl; jj++){
14764 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14765 this.ef[jj] = this.getJsonAccessor(map);
14769 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14770 if(s.totalProperty){
14771 var vt = parseInt(this.getTotal(o), 10);
14776 if(s.successProperty){
14777 var vs = this.getSuccess(o);
14778 if(vs === false || vs === 'false'){
14783 for(var i = 0; i < c; i++){
14786 var id = this.getId(n);
14787 for(var j = 0; j < fl; j++){
14789 var v = this.ef[j](n);
14791 Roo.log('missing convert for ' + f.name);
14795 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14797 var record = new Record(values, id);
14799 records[i] = record;
14805 totalRecords : totalRecords
14808 // used when loading children.. @see loadDataFromChildren
14809 toLoadData: function(rec)
14811 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14812 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14813 return { data : data, total : data.length };
14818 * Ext JS Library 1.1.1
14819 * Copyright(c) 2006-2007, Ext JS, LLC.
14821 * Originally Released Under LGPL - original licence link has changed is not relivant.
14824 * <script type="text/javascript">
14828 * @class Roo.data.ArrayReader
14829 * @extends Roo.data.DataReader
14830 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14831 * Each element of that Array represents a row of data fields. The
14832 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14833 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14837 var RecordDef = Roo.data.Record.create([
14838 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14839 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14841 var myReader = new Roo.data.ArrayReader({
14842 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14846 * This would consume an Array like this:
14848 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14852 * Create a new JsonReader
14853 * @param {Object} meta Metadata configuration options.
14854 * @param {Object|Array} recordType Either an Array of field definition objects
14856 * @cfg {Array} fields Array of field definition objects
14857 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14858 * as specified to {@link Roo.data.Record#create},
14859 * or an {@link Roo.data.Record} object
14862 * created using {@link Roo.data.Record#create}.
14864 Roo.data.ArrayReader = function(meta, recordType)
14866 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14869 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14872 * Create a data block containing Roo.data.Records from an XML document.
14873 * @param {Object} o An Array of row objects which represents the dataset.
14874 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14875 * a cache of Roo.data.Records.
14877 readRecords : function(o)
14879 var sid = this.meta ? this.meta.id : null;
14880 var recordType = this.recordType, fields = recordType.prototype.fields;
14883 for(var i = 0; i < root.length; i++){
14886 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14887 for(var j = 0, jlen = fields.length; j < jlen; j++){
14888 var f = fields.items[j];
14889 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14890 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14892 values[f.name] = v;
14894 var record = new recordType(values, id);
14896 records[records.length] = record;
14900 totalRecords : records.length
14903 // used when loading children.. @see loadDataFromChildren
14904 toLoadData: function(rec)
14906 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14907 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14918 * @class Roo.bootstrap.ComboBox
14919 * @extends Roo.bootstrap.TriggerField
14920 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14921 * @cfg {Boolean} append (true|false) default false
14922 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14923 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14924 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14925 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14926 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14927 * @cfg {Boolean} animate default true
14928 * @cfg {Boolean} emptyResultText only for touch device
14929 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14930 * @cfg {String} emptyTitle default ''
14931 * @cfg {Number} width fixed with? experimental
14933 * Create a new ComboBox.
14934 * @param {Object} config Configuration options
14936 Roo.bootstrap.ComboBox = function(config){
14937 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14941 * Fires when the dropdown list is expanded
14942 * @param {Roo.bootstrap.ComboBox} combo This combo box
14947 * Fires when the dropdown list is collapsed
14948 * @param {Roo.bootstrap.ComboBox} combo This combo box
14952 * @event beforeselect
14953 * Fires before a list item is selected. Return false to cancel the selection.
14954 * @param {Roo.bootstrap.ComboBox} combo This combo box
14955 * @param {Roo.data.Record} record The data record returned from the underlying store
14956 * @param {Number} index The index of the selected item in the dropdown list
14958 'beforeselect' : true,
14961 * Fires when a list item is selected
14962 * @param {Roo.bootstrap.ComboBox} combo This combo box
14963 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14964 * @param {Number} index The index of the selected item in the dropdown list
14968 * @event beforequery
14969 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14970 * The event object passed has these properties:
14971 * @param {Roo.bootstrap.ComboBox} combo This combo box
14972 * @param {String} query The query
14973 * @param {Boolean} forceAll true to force "all" query
14974 * @param {Boolean} cancel true to cancel the query
14975 * @param {Object} e The query event object
14977 'beforequery': true,
14980 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14981 * @param {Roo.bootstrap.ComboBox} combo This combo box
14986 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14987 * @param {Roo.bootstrap.ComboBox} combo This combo box
14988 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14993 * Fires when the remove value from the combobox array
14994 * @param {Roo.bootstrap.ComboBox} combo This combo box
14998 * @event afterremove
14999 * Fires when the remove value from the combobox array
15000 * @param {Roo.bootstrap.ComboBox} combo This combo box
15002 'afterremove' : true,
15004 * @event specialfilter
15005 * Fires when specialfilter
15006 * @param {Roo.bootstrap.ComboBox} combo This combo box
15008 'specialfilter' : true,
15011 * Fires when tick the element
15012 * @param {Roo.bootstrap.ComboBox} combo This combo box
15016 * @event touchviewdisplay
15017 * Fires when touch view require special display (default is using displayField)
15018 * @param {Roo.bootstrap.ComboBox} combo This combo box
15019 * @param {Object} cfg set html .
15021 'touchviewdisplay' : true
15026 this.tickItems = [];
15028 this.selectedIndex = -1;
15029 if(this.mode == 'local'){
15030 if(config.queryDelay === undefined){
15031 this.queryDelay = 10;
15033 if(config.minChars === undefined){
15039 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15042 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15043 * rendering into an Roo.Editor, defaults to false)
15046 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15047 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15050 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15053 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15054 * the dropdown list (defaults to undefined, with no header element)
15058 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15062 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15064 listWidth: undefined,
15066 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15067 * mode = 'remote' or 'text' if mode = 'local')
15069 displayField: undefined,
15072 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15073 * mode = 'remote' or 'value' if mode = 'local').
15074 * Note: use of a valueField requires the user make a selection
15075 * in order for a value to be mapped.
15077 valueField: undefined,
15079 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15084 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15085 * field's data value (defaults to the underlying DOM element's name)
15087 hiddenName: undefined,
15089 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15093 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15095 selectedClass: 'active',
15098 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15102 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15103 * anchor positions (defaults to 'tl-bl')
15105 listAlign: 'tl-bl?',
15107 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15111 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15112 * query specified by the allQuery config option (defaults to 'query')
15114 triggerAction: 'query',
15116 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15117 * (defaults to 4, does not apply if editable = false)
15121 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15122 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15126 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15127 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15131 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15132 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15136 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15137 * when editable = true (defaults to false)
15139 selectOnFocus:false,
15141 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15143 queryParam: 'query',
15145 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15146 * when mode = 'remote' (defaults to 'Loading...')
15148 loadingText: 'Loading...',
15150 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15154 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15158 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15159 * traditional select (defaults to true)
15163 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15167 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15171 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15172 * listWidth has a higher value)
15176 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15177 * allow the user to set arbitrary text into the field (defaults to false)
15179 forceSelection:false,
15181 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15182 * if typeAhead = true (defaults to 250)
15184 typeAheadDelay : 250,
15186 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15187 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15189 valueNotFoundText : undefined,
15191 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15193 blockFocus : false,
15196 * @cfg {Boolean} disableClear Disable showing of clear button.
15198 disableClear : false,
15200 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15202 alwaysQuery : false,
15205 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15210 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15212 invalidClass : "has-warning",
15215 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15217 validClass : "has-success",
15220 * @cfg {Boolean} specialFilter (true|false) special filter default false
15222 specialFilter : false,
15225 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15227 mobileTouchView : true,
15230 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15232 useNativeIOS : false,
15235 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15237 mobile_restrict_height : false,
15239 ios_options : false,
15251 btnPosition : 'right',
15252 triggerList : true,
15253 showToggleBtn : true,
15255 emptyResultText: 'Empty',
15256 triggerText : 'Select',
15260 // element that contains real text value.. (when hidden is used..)
15262 getAutoCreate : function()
15267 * Render classic select for iso
15270 if(Roo.isIOS && this.useNativeIOS){
15271 cfg = this.getAutoCreateNativeIOS();
15279 if(Roo.isTouch && this.mobileTouchView){
15280 cfg = this.getAutoCreateTouchView();
15287 if(!this.tickable){
15288 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15293 * ComboBox with tickable selections
15296 var align = this.labelAlign || this.parentLabelAlign();
15299 cls : 'form-group roo-combobox-tickable' //input-group
15302 var btn_text_select = '';
15303 var btn_text_done = '';
15304 var btn_text_cancel = '';
15306 if (this.btn_text_show) {
15307 btn_text_select = 'Select';
15308 btn_text_done = 'Done';
15309 btn_text_cancel = 'Cancel';
15314 cls : 'tickable-buttons',
15319 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15320 //html : this.triggerText
15321 html: btn_text_select
15327 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15329 html: btn_text_done
15335 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15337 html: btn_text_cancel
15343 buttons.cn.unshift({
15345 cls: 'roo-select2-search-field-input'
15351 Roo.each(buttons.cn, function(c){
15353 c.cls += ' btn-' + _this.size;
15356 if (_this.disabled) {
15363 style : 'display: contents',
15368 cls: 'form-hidden-field'
15372 cls: 'roo-select2-choices',
15376 cls: 'roo-select2-search-field',
15387 cls: 'roo-select2-container input-group roo-select2-container-multi',
15393 // cls: 'typeahead typeahead-long dropdown-menu',
15394 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15399 if(this.hasFeedback && !this.allowBlank){
15403 cls: 'glyphicon form-control-feedback'
15406 combobox.cn.push(feedback);
15413 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15414 tooltip : 'This field is required'
15416 if (Roo.bootstrap.version == 4) {
15419 style : 'display:none'
15422 if (align ==='left' && this.fieldLabel.length) {
15424 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15431 cls : 'control-label col-form-label',
15432 html : this.fieldLabel
15444 var labelCfg = cfg.cn[1];
15445 var contentCfg = cfg.cn[2];
15448 if(this.indicatorpos == 'right'){
15454 cls : 'control-label col-form-label',
15458 html : this.fieldLabel
15474 labelCfg = cfg.cn[0];
15475 contentCfg = cfg.cn[1];
15479 if(this.labelWidth > 12){
15480 labelCfg.style = "width: " + this.labelWidth + 'px';
15482 if(this.width * 1 > 0){
15483 contentCfg.style = "width: " + this.width + 'px';
15485 if(this.labelWidth < 13 && this.labelmd == 0){
15486 this.labelmd = this.labelWidth;
15489 if(this.labellg > 0){
15490 labelCfg.cls += ' col-lg-' + this.labellg;
15491 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15494 if(this.labelmd > 0){
15495 labelCfg.cls += ' col-md-' + this.labelmd;
15496 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15499 if(this.labelsm > 0){
15500 labelCfg.cls += ' col-sm-' + this.labelsm;
15501 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15504 if(this.labelxs > 0){
15505 labelCfg.cls += ' col-xs-' + this.labelxs;
15506 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15510 } else if ( this.fieldLabel.length) {
15511 // Roo.log(" label");
15516 //cls : 'input-group-addon',
15517 html : this.fieldLabel
15522 if(this.indicatorpos == 'right'){
15526 //cls : 'input-group-addon',
15527 html : this.fieldLabel
15537 // Roo.log(" no label && no align");
15544 ['xs','sm','md','lg'].map(function(size){
15545 if (settings[size]) {
15546 cfg.cls += ' col-' + size + '-' + settings[size];
15554 _initEventsCalled : false,
15557 initEvents: function()
15559 if (this._initEventsCalled) { // as we call render... prevent looping...
15562 this._initEventsCalled = true;
15565 throw "can not find store for combo";
15568 this.indicator = this.indicatorEl();
15570 this.store = Roo.factory(this.store, Roo.data);
15571 this.store.parent = this;
15573 // if we are building from html. then this element is so complex, that we can not really
15574 // use the rendered HTML.
15575 // so we have to trash and replace the previous code.
15576 if (Roo.XComponent.build_from_html) {
15577 // remove this element....
15578 var e = this.el.dom, k=0;
15579 while (e ) { e = e.previousSibling; ++k;}
15584 this.rendered = false;
15586 this.render(this.parent().getChildContainer(true), k);
15589 if(Roo.isIOS && this.useNativeIOS){
15590 this.initIOSView();
15598 if(Roo.isTouch && this.mobileTouchView){
15599 this.initTouchView();
15604 this.initTickableEvents();
15608 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15610 if(this.hiddenName){
15612 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15614 this.hiddenField.dom.value =
15615 this.hiddenValue !== undefined ? this.hiddenValue :
15616 this.value !== undefined ? this.value : '';
15618 // prevent input submission
15619 this.el.dom.removeAttribute('name');
15620 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15625 // this.el.dom.setAttribute('autocomplete', 'off');
15628 var cls = 'x-combo-list';
15630 //this.list = new Roo.Layer({
15631 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15637 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15638 _this.list.setWidth(lw);
15641 this.list.on('mouseover', this.onViewOver, this);
15642 this.list.on('mousemove', this.onViewMove, this);
15643 this.list.on('scroll', this.onViewScroll, this);
15646 this.list.swallowEvent('mousewheel');
15647 this.assetHeight = 0;
15650 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15651 this.assetHeight += this.header.getHeight();
15654 this.innerList = this.list.createChild({cls:cls+'-inner'});
15655 this.innerList.on('mouseover', this.onViewOver, this);
15656 this.innerList.on('mousemove', this.onViewMove, this);
15657 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15659 if(this.allowBlank && !this.pageSize && !this.disableClear){
15660 this.footer = this.list.createChild({cls:cls+'-ft'});
15661 this.pageTb = new Roo.Toolbar(this.footer);
15665 this.footer = this.list.createChild({cls:cls+'-ft'});
15666 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15667 {pageSize: this.pageSize});
15671 if (this.pageTb && this.allowBlank && !this.disableClear) {
15673 this.pageTb.add(new Roo.Toolbar.Fill(), {
15674 cls: 'x-btn-icon x-btn-clear',
15676 handler: function()
15679 _this.clearValue();
15680 _this.onSelect(false, -1);
15685 this.assetHeight += this.footer.getHeight();
15690 this.tpl = Roo.bootstrap.version == 4 ?
15691 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15692 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15695 this.view = new Roo.View(this.list, this.tpl, {
15696 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15698 //this.view.wrapEl.setDisplayed(false);
15699 this.view.on('click', this.onViewClick, this);
15702 this.store.on('beforeload', this.onBeforeLoad, this);
15703 this.store.on('load', this.onLoad, this);
15704 this.store.on('loadexception', this.onLoadException, this);
15706 if(this.resizable){
15707 this.resizer = new Roo.Resizable(this.list, {
15708 pinned:true, handles:'se'
15710 this.resizer.on('resize', function(r, w, h){
15711 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15712 this.listWidth = w;
15713 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15714 this.restrictHeight();
15716 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15719 if(!this.editable){
15720 this.editable = true;
15721 this.setEditable(false);
15726 if (typeof(this.events.add.listeners) != 'undefined') {
15728 this.addicon = this.wrap.createChild(
15729 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15731 this.addicon.on('click', function(e) {
15732 this.fireEvent('add', this);
15735 if (typeof(this.events.edit.listeners) != 'undefined') {
15737 this.editicon = this.wrap.createChild(
15738 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15739 if (this.addicon) {
15740 this.editicon.setStyle('margin-left', '40px');
15742 this.editicon.on('click', function(e) {
15744 // we fire even if inothing is selected..
15745 this.fireEvent('edit', this, this.lastData );
15751 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15752 "up" : function(e){
15753 this.inKeyMode = true;
15757 "down" : function(e){
15758 if(!this.isExpanded()){
15759 this.onTriggerClick();
15761 this.inKeyMode = true;
15766 "enter" : function(e){
15767 // this.onViewClick();
15771 if(this.fireEvent("specialkey", this, e)){
15772 this.onViewClick(false);
15778 "esc" : function(e){
15782 "tab" : function(e){
15785 if(this.fireEvent("specialkey", this, e)){
15786 this.onViewClick(false);
15794 doRelay : function(foo, bar, hname){
15795 if(hname == 'down' || this.scope.isExpanded()){
15796 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15805 this.queryDelay = Math.max(this.queryDelay || 10,
15806 this.mode == 'local' ? 10 : 250);
15809 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15811 if(this.typeAhead){
15812 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15814 if(this.editable !== false){
15815 this.inputEl().on("keyup", this.onKeyUp, this);
15817 if(this.forceSelection){
15818 this.inputEl().on('blur', this.doForce, this);
15822 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15823 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827 initTickableEvents: function()
15831 if(this.hiddenName){
15833 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15835 this.hiddenField.dom.value =
15836 this.hiddenValue !== undefined ? this.hiddenValue :
15837 this.value !== undefined ? this.value : '';
15839 // prevent input submission
15840 this.el.dom.removeAttribute('name');
15841 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15846 // this.list = this.el.select('ul.dropdown-menu',true).first();
15848 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15849 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15850 if(this.triggerList){
15851 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15854 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15855 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15857 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15858 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15860 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15861 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15863 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15864 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15865 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15868 this.cancelBtn.hide();
15873 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15874 _this.list.setWidth(lw);
15877 this.list.on('mouseover', this.onViewOver, this);
15878 this.list.on('mousemove', this.onViewMove, this);
15880 this.list.on('scroll', this.onViewScroll, this);
15883 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15884 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15887 this.view = new Roo.View(this.list, this.tpl, {
15892 selectedClass: this.selectedClass
15895 //this.view.wrapEl.setDisplayed(false);
15896 this.view.on('click', this.onViewClick, this);
15900 this.store.on('beforeload', this.onBeforeLoad, this);
15901 this.store.on('load', this.onLoad, this);
15902 this.store.on('loadexception', this.onLoadException, this);
15905 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15906 "up" : function(e){
15907 this.inKeyMode = true;
15911 "down" : function(e){
15912 this.inKeyMode = true;
15916 "enter" : function(e){
15917 if(this.fireEvent("specialkey", this, e)){
15918 this.onViewClick(false);
15924 "esc" : function(e){
15925 this.onTickableFooterButtonClick(e, false, false);
15928 "tab" : function(e){
15929 this.fireEvent("specialkey", this, e);
15931 this.onTickableFooterButtonClick(e, false, false);
15938 doRelay : function(e, fn, key){
15939 if(this.scope.isExpanded()){
15940 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15949 this.queryDelay = Math.max(this.queryDelay || 10,
15950 this.mode == 'local' ? 10 : 250);
15953 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15955 if(this.typeAhead){
15956 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15959 if(this.editable !== false){
15960 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15963 this.indicator = this.indicatorEl();
15965 if(this.indicator){
15966 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15967 this.indicator.hide();
15972 onDestroy : function(){
15974 this.view.setStore(null);
15975 this.view.el.removeAllListeners();
15976 this.view.el.remove();
15977 this.view.purgeListeners();
15980 this.list.dom.innerHTML = '';
15984 this.store.un('beforeload', this.onBeforeLoad, this);
15985 this.store.un('load', this.onLoad, this);
15986 this.store.un('loadexception', this.onLoadException, this);
15988 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15992 fireKey : function(e){
15993 if(e.isNavKeyPress() && !this.list.isVisible()){
15994 this.fireEvent("specialkey", this, e);
15999 onResize: function(w, h)
16003 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16005 // if(typeof w != 'number'){
16006 // // we do not handle it!?!?
16009 // var tw = this.trigger.getWidth();
16010 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16011 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16013 // this.inputEl().setWidth( this.adjustWidth('input', x));
16015 // //this.trigger.setStyle('left', x+'px');
16017 // if(this.list && this.listWidth === undefined){
16018 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16019 // this.list.setWidth(lw);
16020 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16028 * Allow or prevent the user from directly editing the field text. If false is passed,
16029 * the user will only be able to select from the items defined in the dropdown list. This method
16030 * is the runtime equivalent of setting the 'editable' config option at config time.
16031 * @param {Boolean} value True to allow the user to directly edit the field text
16033 setEditable : function(value){
16034 if(value == this.editable){
16037 this.editable = value;
16039 this.inputEl().dom.setAttribute('readOnly', true);
16040 this.inputEl().on('mousedown', this.onTriggerClick, this);
16041 this.inputEl().addClass('x-combo-noedit');
16043 this.inputEl().dom.setAttribute('readOnly', false);
16044 this.inputEl().un('mousedown', this.onTriggerClick, this);
16045 this.inputEl().removeClass('x-combo-noedit');
16051 onBeforeLoad : function(combo,opts){
16052 if(!this.hasFocus){
16056 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16058 this.restrictHeight();
16059 this.selectedIndex = -1;
16063 onLoad : function(){
16065 this.hasQuery = false;
16067 if(!this.hasFocus){
16071 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16072 this.loading.hide();
16075 if(this.store.getCount() > 0){
16078 this.restrictHeight();
16079 if(this.lastQuery == this.allQuery){
16080 if(this.editable && !this.tickable){
16081 this.inputEl().dom.select();
16085 !this.selectByValue(this.value, true) &&
16088 !this.store.lastOptions ||
16089 typeof(this.store.lastOptions.add) == 'undefined' ||
16090 this.store.lastOptions.add != true
16093 this.select(0, true);
16096 if(this.autoFocus){
16099 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16100 this.taTask.delay(this.typeAheadDelay);
16104 this.onEmptyResults();
16110 onLoadException : function()
16112 this.hasQuery = false;
16114 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16115 this.loading.hide();
16118 if(this.tickable && this.editable){
16123 // only causes errors at present
16124 //Roo.log(this.store.reader.jsonData);
16125 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16127 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16133 onTypeAhead : function(){
16134 if(this.store.getCount() > 0){
16135 var r = this.store.getAt(0);
16136 var newValue = r.data[this.displayField];
16137 var len = newValue.length;
16138 var selStart = this.getRawValue().length;
16140 if(selStart != len){
16141 this.setRawValue(newValue);
16142 this.selectText(selStart, newValue.length);
16148 onSelect : function(record, index){
16150 if(this.fireEvent('beforeselect', this, record, index) !== false){
16152 this.setFromData(index > -1 ? record.data : false);
16155 this.fireEvent('select', this, record, index);
16160 * Returns the currently selected field value or empty string if no value is set.
16161 * @return {String} value The selected value
16163 getValue : function()
16165 if(Roo.isIOS && this.useNativeIOS){
16166 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16170 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16173 if(this.valueField){
16174 return typeof this.value != 'undefined' ? this.value : '';
16176 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16180 getRawValue : function()
16182 if(Roo.isIOS && this.useNativeIOS){
16183 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16186 var v = this.inputEl().getValue();
16192 * Clears any text/value currently set in the field
16194 clearValue : function(){
16196 if(this.hiddenField){
16197 this.hiddenField.dom.value = '';
16200 this.setRawValue('');
16201 this.lastSelectionText = '';
16202 this.lastData = false;
16204 var close = this.closeTriggerEl();
16215 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16216 * will be displayed in the field. If the value does not match the data value of an existing item,
16217 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16218 * Otherwise the field will be blank (although the value will still be set).
16219 * @param {String} value The value to match
16221 setValue : function(v)
16223 if(Roo.isIOS && this.useNativeIOS){
16224 this.setIOSValue(v);
16234 if(this.valueField){
16235 var r = this.findRecord(this.valueField, v);
16237 text = r.data[this.displayField];
16238 }else if(this.valueNotFoundText !== undefined){
16239 text = this.valueNotFoundText;
16242 this.lastSelectionText = text;
16243 if(this.hiddenField){
16244 this.hiddenField.dom.value = v;
16246 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16249 var close = this.closeTriggerEl();
16252 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16258 * @property {Object} the last set data for the element
16263 * Sets the value of the field based on a object which is related to the record format for the store.
16264 * @param {Object} value the value to set as. or false on reset?
16266 setFromData : function(o){
16273 var dv = ''; // display value
16274 var vv = ''; // value value..
16276 if (this.displayField) {
16277 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16279 // this is an error condition!!!
16280 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16283 if(this.valueField){
16284 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16287 var close = this.closeTriggerEl();
16290 if(dv.length || vv * 1 > 0){
16292 this.blockFocus=true;
16298 if(this.hiddenField){
16299 this.hiddenField.dom.value = vv;
16301 this.lastSelectionText = dv;
16302 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16306 // no hidden field.. - we store the value in 'value', but still display
16307 // display field!!!!
16308 this.lastSelectionText = dv;
16309 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16316 reset : function(){
16317 // overridden so that last data is reset..
16324 this.setValue(this.originalValue);
16325 //this.clearInvalid();
16326 this.lastData = false;
16328 this.view.clearSelections();
16334 findRecord : function(prop, value){
16336 if(this.store.getCount() > 0){
16337 this.store.each(function(r){
16338 if(r.data[prop] == value){
16348 getName: function()
16350 // returns hidden if it's set..
16351 if (!this.rendered) {return ''};
16352 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16356 onViewMove : function(e, t){
16357 this.inKeyMode = false;
16361 onViewOver : function(e, t){
16362 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16365 var item = this.view.findItemFromChild(t);
16368 var index = this.view.indexOf(item);
16369 this.select(index, false);
16374 onViewClick : function(view, doFocus, el, e)
16376 var index = this.view.getSelectedIndexes()[0];
16378 var r = this.store.getAt(index);
16382 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16389 Roo.each(this.tickItems, function(v,k){
16391 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16393 _this.tickItems.splice(k, 1);
16395 if(typeof(e) == 'undefined' && view == false){
16396 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16408 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16409 this.tickItems.push(r.data);
16412 if(typeof(e) == 'undefined' && view == false){
16413 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16420 this.onSelect(r, index);
16422 if(doFocus !== false && !this.blockFocus){
16423 this.inputEl().focus();
16428 restrictHeight : function(){
16429 //this.innerList.dom.style.height = '';
16430 //var inner = this.innerList.dom;
16431 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16432 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16433 //this.list.beginUpdate();
16434 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16435 this.list.alignTo(this.inputEl(), this.listAlign);
16436 this.list.alignTo(this.inputEl(), this.listAlign);
16437 //this.list.endUpdate();
16441 onEmptyResults : function(){
16443 if(this.tickable && this.editable){
16444 this.hasFocus = false;
16445 this.restrictHeight();
16453 * Returns true if the dropdown list is expanded, else false.
16455 isExpanded : function(){
16456 return this.list.isVisible();
16460 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16461 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16462 * @param {String} value The data value of the item to select
16463 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16464 * selected item if it is not currently in view (defaults to true)
16465 * @return {Boolean} True if the value matched an item in the list, else false
16467 selectByValue : function(v, scrollIntoView){
16468 if(v !== undefined && v !== null){
16469 var r = this.findRecord(this.valueField || this.displayField, v);
16471 this.select(this.store.indexOf(r), scrollIntoView);
16479 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16480 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16481 * @param {Number} index The zero-based index of the list item to select
16482 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16483 * selected item if it is not currently in view (defaults to true)
16485 select : function(index, scrollIntoView){
16486 this.selectedIndex = index;
16487 this.view.select(index);
16488 if(scrollIntoView !== false){
16489 var el = this.view.getNode(index);
16491 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16494 this.list.scrollChildIntoView(el, false);
16500 selectNext : function(){
16501 var ct = this.store.getCount();
16503 if(this.selectedIndex == -1){
16505 }else if(this.selectedIndex < ct-1){
16506 this.select(this.selectedIndex+1);
16512 selectPrev : function(){
16513 var ct = this.store.getCount();
16515 if(this.selectedIndex == -1){
16517 }else if(this.selectedIndex != 0){
16518 this.select(this.selectedIndex-1);
16524 onKeyUp : function(e){
16525 if(this.editable !== false && !e.isSpecialKey()){
16526 this.lastKey = e.getKey();
16527 this.dqTask.delay(this.queryDelay);
16532 validateBlur : function(){
16533 return !this.list || !this.list.isVisible();
16537 initQuery : function(){
16539 var v = this.getRawValue();
16541 if(this.tickable && this.editable){
16542 v = this.tickableInputEl().getValue();
16549 doForce : function(){
16550 if(this.inputEl().dom.value.length > 0){
16551 this.inputEl().dom.value =
16552 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16558 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16559 * query allowing the query action to be canceled if needed.
16560 * @param {String} query The SQL query to execute
16561 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16562 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16563 * saved in the current store (defaults to false)
16565 doQuery : function(q, forceAll){
16567 if(q === undefined || q === null){
16572 forceAll: forceAll,
16576 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16581 forceAll = qe.forceAll;
16582 if(forceAll === true || (q.length >= this.minChars)){
16584 this.hasQuery = true;
16586 if(this.lastQuery != q || this.alwaysQuery){
16587 this.lastQuery = q;
16588 if(this.mode == 'local'){
16589 this.selectedIndex = -1;
16591 this.store.clearFilter();
16594 if(this.specialFilter){
16595 this.fireEvent('specialfilter', this);
16600 this.store.filter(this.displayField, q);
16603 this.store.fireEvent("datachanged", this.store);
16610 this.store.baseParams[this.queryParam] = q;
16612 var options = {params : this.getParams(q)};
16615 options.add = true;
16616 options.params.start = this.page * this.pageSize;
16619 this.store.load(options);
16622 * this code will make the page width larger, at the beginning, the list not align correctly,
16623 * we should expand the list on onLoad
16624 * so command out it
16629 this.selectedIndex = -1;
16634 this.loadNext = false;
16638 getParams : function(q){
16640 //p[this.queryParam] = q;
16644 p.limit = this.pageSize;
16650 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16652 collapse : function(){
16653 if(!this.isExpanded()){
16659 this.hasFocus = false;
16663 this.cancelBtn.hide();
16664 this.trigger.show();
16667 this.tickableInputEl().dom.value = '';
16668 this.tickableInputEl().blur();
16673 Roo.get(document).un('mousedown', this.collapseIf, this);
16674 Roo.get(document).un('mousewheel', this.collapseIf, this);
16675 if (!this.editable) {
16676 Roo.get(document).un('keydown', this.listKeyPress, this);
16678 this.fireEvent('collapse', this);
16684 collapseIf : function(e){
16685 var in_combo = e.within(this.el);
16686 var in_list = e.within(this.list);
16687 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16689 if (in_combo || in_list || is_list) {
16690 //e.stopPropagation();
16695 this.onTickableFooterButtonClick(e, false, false);
16703 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16705 expand : function(){
16707 if(this.isExpanded() || !this.hasFocus){
16711 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16712 this.list.setWidth(lw);
16718 this.restrictHeight();
16722 this.tickItems = Roo.apply([], this.item);
16725 this.cancelBtn.show();
16726 this.trigger.hide();
16729 this.tickableInputEl().focus();
16734 Roo.get(document).on('mousedown', this.collapseIf, this);
16735 Roo.get(document).on('mousewheel', this.collapseIf, this);
16736 if (!this.editable) {
16737 Roo.get(document).on('keydown', this.listKeyPress, this);
16740 this.fireEvent('expand', this);
16744 // Implements the default empty TriggerField.onTriggerClick function
16745 onTriggerClick : function(e)
16747 Roo.log('trigger click');
16749 if(this.disabled || !this.triggerList){
16754 this.loadNext = false;
16756 if(this.isExpanded()){
16758 if (!this.blockFocus) {
16759 this.inputEl().focus();
16763 this.hasFocus = true;
16764 if(this.triggerAction == 'all') {
16765 this.doQuery(this.allQuery, true);
16767 this.doQuery(this.getRawValue());
16769 if (!this.blockFocus) {
16770 this.inputEl().focus();
16775 onTickableTriggerClick : function(e)
16782 this.loadNext = false;
16783 this.hasFocus = true;
16785 if(this.triggerAction == 'all') {
16786 this.doQuery(this.allQuery, true);
16788 this.doQuery(this.getRawValue());
16792 onSearchFieldClick : function(e)
16794 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16795 this.onTickableFooterButtonClick(e, false, false);
16799 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16804 this.loadNext = false;
16805 this.hasFocus = true;
16807 if(this.triggerAction == 'all') {
16808 this.doQuery(this.allQuery, true);
16810 this.doQuery(this.getRawValue());
16814 listKeyPress : function(e)
16816 //Roo.log('listkeypress');
16817 // scroll to first matching element based on key pres..
16818 if (e.isSpecialKey()) {
16821 var k = String.fromCharCode(e.getKey()).toUpperCase();
16824 var csel = this.view.getSelectedNodes();
16825 var cselitem = false;
16827 var ix = this.view.indexOf(csel[0]);
16828 cselitem = this.store.getAt(ix);
16829 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16835 this.store.each(function(v) {
16837 // start at existing selection.
16838 if (cselitem.id == v.id) {
16844 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16845 match = this.store.indexOf(v);
16851 if (match === false) {
16852 return true; // no more action?
16855 this.view.select(match);
16856 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16857 sn.scrollIntoView(sn.dom.parentNode, false);
16860 onViewScroll : function(e, t){
16862 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){
16866 this.hasQuery = true;
16868 this.loading = this.list.select('.loading', true).first();
16870 if(this.loading === null){
16871 this.list.createChild({
16873 cls: 'loading roo-select2-more-results roo-select2-active',
16874 html: 'Loading more results...'
16877 this.loading = this.list.select('.loading', true).first();
16879 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16881 this.loading.hide();
16884 this.loading.show();
16889 this.loadNext = true;
16891 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16896 addItem : function(o)
16898 var dv = ''; // display value
16900 if (this.displayField) {
16901 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16903 // this is an error condition!!!
16904 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16911 var choice = this.choices.createChild({
16913 cls: 'roo-select2-search-choice',
16922 cls: 'roo-select2-search-choice-close fa fa-times',
16927 }, this.searchField);
16929 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16931 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16939 this.inputEl().dom.value = '';
16944 onRemoveItem : function(e, _self, o)
16946 e.preventDefault();
16948 this.lastItem = Roo.apply([], this.item);
16950 var index = this.item.indexOf(o.data) * 1;
16953 Roo.log('not this item?!');
16957 this.item.splice(index, 1);
16962 this.fireEvent('remove', this, e);
16968 syncValue : function()
16970 if(!this.item.length){
16977 Roo.each(this.item, function(i){
16978 if(_this.valueField){
16979 value.push(i[_this.valueField]);
16986 this.value = value.join(',');
16988 if(this.hiddenField){
16989 this.hiddenField.dom.value = this.value;
16992 this.store.fireEvent("datachanged", this.store);
16997 clearItem : function()
16999 if(!this.multiple){
17005 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17013 if(this.tickable && !Roo.isTouch){
17014 this.view.refresh();
17018 inputEl: function ()
17020 if(Roo.isIOS && this.useNativeIOS){
17021 return this.el.select('select.roo-ios-select', true).first();
17024 if(Roo.isTouch && this.mobileTouchView){
17025 return this.el.select('input.form-control',true).first();
17029 return this.searchField;
17032 return this.el.select('input.form-control',true).first();
17035 onTickableFooterButtonClick : function(e, btn, el)
17037 e.preventDefault();
17039 this.lastItem = Roo.apply([], this.item);
17041 if(btn && btn.name == 'cancel'){
17042 this.tickItems = Roo.apply([], this.item);
17051 Roo.each(this.tickItems, function(o){
17059 validate : function()
17061 if(this.getVisibilityEl().hasClass('hidden')){
17065 var v = this.getRawValue();
17068 v = this.getValue();
17071 if(this.disabled || this.allowBlank || v.length){
17076 this.markInvalid();
17080 tickableInputEl : function()
17082 if(!this.tickable || !this.editable){
17083 return this.inputEl();
17086 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17090 getAutoCreateTouchView : function()
17095 cls: 'form-group' //input-group
17101 type : this.inputType,
17102 cls : 'form-control x-combo-noedit',
17103 autocomplete: 'new-password',
17104 placeholder : this.placeholder || '',
17109 input.name = this.name;
17113 input.cls += ' input-' + this.size;
17116 if (this.disabled) {
17117 input.disabled = true;
17121 cls : 'roo-combobox-wrap',
17128 inputblock.cls += ' input-group';
17130 inputblock.cn.unshift({
17132 cls : 'input-group-addon input-group-prepend input-group-text',
17137 if(this.removable && !this.multiple){
17138 inputblock.cls += ' roo-removable';
17140 inputblock.cn.push({
17143 cls : 'roo-combo-removable-btn close'
17147 if(this.hasFeedback && !this.allowBlank){
17149 inputblock.cls += ' has-feedback';
17151 inputblock.cn.push({
17153 cls: 'glyphicon form-control-feedback'
17160 inputblock.cls += (this.before) ? '' : ' input-group';
17162 inputblock.cn.push({
17164 cls : 'input-group-addon input-group-append input-group-text',
17170 var ibwrap = inputblock;
17175 cls: 'roo-select2-choices',
17179 cls: 'roo-select2-search-field',
17192 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17197 cls: 'form-hidden-field'
17203 if(!this.multiple && this.showToggleBtn){
17209 if (this.caret != false) {
17212 cls: 'fa fa-' + this.caret
17219 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17221 Roo.bootstrap.version == 3 ? caret : '',
17224 cls: 'combobox-clear',
17238 combobox.cls += ' roo-select2-container-multi';
17241 var align = this.labelAlign || this.parentLabelAlign();
17243 if (align ==='left' && this.fieldLabel.length) {
17248 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17249 tooltip : 'This field is required'
17253 cls : 'control-label col-form-label',
17254 html : this.fieldLabel
17258 cls : 'roo-combobox-wrap ',
17265 var labelCfg = cfg.cn[1];
17266 var contentCfg = cfg.cn[2];
17269 if(this.indicatorpos == 'right'){
17274 cls : 'control-label col-form-label',
17278 html : this.fieldLabel
17282 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17283 tooltip : 'This field is required'
17288 cls : "roo-combobox-wrap ",
17296 labelCfg = cfg.cn[0];
17297 contentCfg = cfg.cn[1];
17302 if(this.labelWidth > 12){
17303 labelCfg.style = "width: " + this.labelWidth + 'px';
17306 if(this.labelWidth < 13 && this.labelmd == 0){
17307 this.labelmd = this.labelWidth;
17310 if(this.labellg > 0){
17311 labelCfg.cls += ' col-lg-' + this.labellg;
17312 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17315 if(this.labelmd > 0){
17316 labelCfg.cls += ' col-md-' + this.labelmd;
17317 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17320 if(this.labelsm > 0){
17321 labelCfg.cls += ' col-sm-' + this.labelsm;
17322 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17325 if(this.labelxs > 0){
17326 labelCfg.cls += ' col-xs-' + this.labelxs;
17327 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17331 } else if ( this.fieldLabel.length) {
17335 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17336 tooltip : 'This field is required'
17340 cls : 'control-label',
17341 html : this.fieldLabel
17352 if(this.indicatorpos == 'right'){
17356 cls : 'control-label',
17357 html : this.fieldLabel,
17361 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17362 tooltip : 'This field is required'
17379 var settings = this;
17381 ['xs','sm','md','lg'].map(function(size){
17382 if (settings[size]) {
17383 cfg.cls += ' col-' + size + '-' + settings[size];
17390 initTouchView : function()
17392 this.renderTouchView();
17394 this.touchViewEl.on('scroll', function(){
17395 this.el.dom.scrollTop = 0;
17398 this.originalValue = this.getValue();
17400 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17402 this.inputEl().on("click", this.showTouchView, this);
17403 if (this.triggerEl) {
17404 this.triggerEl.on("click", this.showTouchView, this);
17408 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17409 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17411 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17413 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17414 this.store.on('load', this.onTouchViewLoad, this);
17415 this.store.on('loadexception', this.onTouchViewLoadException, this);
17417 if(this.hiddenName){
17419 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17421 this.hiddenField.dom.value =
17422 this.hiddenValue !== undefined ? this.hiddenValue :
17423 this.value !== undefined ? this.value : '';
17425 this.el.dom.removeAttribute('name');
17426 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17430 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17431 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17434 if(this.removable && !this.multiple){
17435 var close = this.closeTriggerEl();
17437 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17438 close.on('click', this.removeBtnClick, this, close);
17442 * fix the bug in Safari iOS8
17444 this.inputEl().on("focus", function(e){
17445 document.activeElement.blur();
17448 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17455 renderTouchView : function()
17457 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17458 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17460 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17461 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17463 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17464 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17465 this.touchViewBodyEl.setStyle('overflow', 'auto');
17467 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17468 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17470 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17471 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17475 showTouchView : function()
17481 this.touchViewHeaderEl.hide();
17483 if(this.modalTitle.length){
17484 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17485 this.touchViewHeaderEl.show();
17488 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17489 this.touchViewEl.show();
17491 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17493 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17494 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17496 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17498 if(this.modalTitle.length){
17499 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17502 this.touchViewBodyEl.setHeight(bodyHeight);
17506 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17508 this.touchViewEl.addClass(['in','show']);
17511 if(this._touchViewMask){
17512 Roo.get(document.body).addClass("x-body-masked");
17513 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17514 this._touchViewMask.setStyle('z-index', 10000);
17515 this._touchViewMask.addClass('show');
17518 this.doTouchViewQuery();
17522 hideTouchView : function()
17524 this.touchViewEl.removeClass(['in','show']);
17528 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17530 this.touchViewEl.setStyle('display', 'none');
17533 if(this._touchViewMask){
17534 this._touchViewMask.removeClass('show');
17535 Roo.get(document.body).removeClass("x-body-masked");
17539 setTouchViewValue : function()
17546 Roo.each(this.tickItems, function(o){
17551 this.hideTouchView();
17554 doTouchViewQuery : function()
17563 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17567 if(!this.alwaysQuery || this.mode == 'local'){
17568 this.onTouchViewLoad();
17575 onTouchViewBeforeLoad : function(combo,opts)
17581 onTouchViewLoad : function()
17583 if(this.store.getCount() < 1){
17584 this.onTouchViewEmptyResults();
17588 this.clearTouchView();
17590 var rawValue = this.getRawValue();
17592 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17594 this.tickItems = [];
17596 this.store.data.each(function(d, rowIndex){
17597 var row = this.touchViewListGroup.createChild(template);
17599 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17600 row.addClass(d.data.cls);
17603 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17606 html : d.data[this.displayField]
17609 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17610 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17613 row.removeClass('selected');
17614 if(!this.multiple && this.valueField &&
17615 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17618 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17619 row.addClass('selected');
17622 if(this.multiple && this.valueField &&
17623 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17627 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17628 this.tickItems.push(d.data);
17631 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17635 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17637 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17639 if(this.modalTitle.length){
17640 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17643 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17645 if(this.mobile_restrict_height && listHeight < bodyHeight){
17646 this.touchViewBodyEl.setHeight(listHeight);
17651 if(firstChecked && listHeight > bodyHeight){
17652 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17657 onTouchViewLoadException : function()
17659 this.hideTouchView();
17662 onTouchViewEmptyResults : function()
17664 this.clearTouchView();
17666 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17668 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17672 clearTouchView : function()
17674 this.touchViewListGroup.dom.innerHTML = '';
17677 onTouchViewClick : function(e, el, o)
17679 e.preventDefault();
17682 var rowIndex = o.rowIndex;
17684 var r = this.store.getAt(rowIndex);
17686 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17688 if(!this.multiple){
17689 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17690 c.dom.removeAttribute('checked');
17693 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17695 this.setFromData(r.data);
17697 var close = this.closeTriggerEl();
17703 this.hideTouchView();
17705 this.fireEvent('select', this, r, rowIndex);
17710 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17711 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17712 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17716 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17717 this.addItem(r.data);
17718 this.tickItems.push(r.data);
17722 getAutoCreateNativeIOS : function()
17725 cls: 'form-group' //input-group,
17730 cls : 'roo-ios-select'
17734 combobox.name = this.name;
17737 if (this.disabled) {
17738 combobox.disabled = true;
17741 var settings = this;
17743 ['xs','sm','md','lg'].map(function(size){
17744 if (settings[size]) {
17745 cfg.cls += ' col-' + size + '-' + settings[size];
17755 initIOSView : function()
17757 this.store.on('load', this.onIOSViewLoad, this);
17762 onIOSViewLoad : function()
17764 if(this.store.getCount() < 1){
17768 this.clearIOSView();
17770 if(this.allowBlank) {
17772 var default_text = '-- SELECT --';
17774 if(this.placeholder.length){
17775 default_text = this.placeholder;
17778 if(this.emptyTitle.length){
17779 default_text += ' - ' + this.emptyTitle + ' -';
17782 var opt = this.inputEl().createChild({
17785 html : default_text
17789 o[this.valueField] = 0;
17790 o[this.displayField] = default_text;
17792 this.ios_options.push({
17799 this.store.data.each(function(d, rowIndex){
17803 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17804 html = d.data[this.displayField];
17809 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17810 value = d.data[this.valueField];
17819 if(this.value == d.data[this.valueField]){
17820 option['selected'] = true;
17823 var opt = this.inputEl().createChild(option);
17825 this.ios_options.push({
17832 this.inputEl().on('change', function(){
17833 this.fireEvent('select', this);
17838 clearIOSView: function()
17840 this.inputEl().dom.innerHTML = '';
17842 this.ios_options = [];
17845 setIOSValue: function(v)
17849 if(!this.ios_options){
17853 Roo.each(this.ios_options, function(opts){
17855 opts.el.dom.removeAttribute('selected');
17857 if(opts.data[this.valueField] != v){
17861 opts.el.dom.setAttribute('selected', true);
17867 * @cfg {Boolean} grow
17871 * @cfg {Number} growMin
17875 * @cfg {Number} growMax
17884 Roo.apply(Roo.bootstrap.ComboBox, {
17888 cls: 'modal-header',
17910 cls: 'list-group-item',
17914 cls: 'roo-combobox-list-group-item-value'
17918 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17932 listItemCheckbox : {
17934 cls: 'list-group-item',
17938 cls: 'roo-combobox-list-group-item-value'
17942 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17958 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17963 cls: 'modal-footer',
17971 cls: 'col-xs-6 text-left',
17974 cls: 'btn btn-danger roo-touch-view-cancel',
17980 cls: 'col-xs-6 text-right',
17983 cls: 'btn btn-success roo-touch-view-ok',
17994 Roo.apply(Roo.bootstrap.ComboBox, {
17996 touchViewTemplate : {
17998 cls: 'modal fade roo-combobox-touch-view',
18002 cls: 'modal-dialog',
18003 style : 'position:fixed', // we have to fix position....
18007 cls: 'modal-content',
18009 Roo.bootstrap.ComboBox.header,
18010 Roo.bootstrap.ComboBox.body,
18011 Roo.bootstrap.ComboBox.footer
18020 * Ext JS Library 1.1.1
18021 * Copyright(c) 2006-2007, Ext JS, LLC.
18023 * Originally Released Under LGPL - original licence link has changed is not relivant.
18026 * <script type="text/javascript">
18031 * @extends Roo.util.Observable
18032 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18033 * This class also supports single and multi selection modes. <br>
18034 * Create a data model bound view:
18036 var store = new Roo.data.Store(...);
18038 var view = new Roo.View({
18040 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18042 singleSelect: true,
18043 selectedClass: "ydataview-selected",
18047 // listen for node click?
18048 view.on("click", function(vw, index, node, e){
18049 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18053 dataModel.load("foobar.xml");
18055 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18057 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18058 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18060 * Note: old style constructor is still suported (container, template, config)
18063 * Create a new View
18064 * @param {Object} config The config object
18067 Roo.View = function(config, depreciated_tpl, depreciated_config){
18069 this.parent = false;
18071 if (typeof(depreciated_tpl) == 'undefined') {
18072 // new way.. - universal constructor.
18073 Roo.apply(this, config);
18074 this.el = Roo.get(this.el);
18077 this.el = Roo.get(config);
18078 this.tpl = depreciated_tpl;
18079 Roo.apply(this, depreciated_config);
18081 this.wrapEl = this.el.wrap().wrap();
18082 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18085 if(typeof(this.tpl) == "string"){
18086 this.tpl = new Roo.Template(this.tpl);
18088 // support xtype ctors..
18089 this.tpl = new Roo.factory(this.tpl, Roo);
18093 this.tpl.compile();
18098 * @event beforeclick
18099 * Fires before a click is processed. Returns false to cancel the default action.
18100 * @param {Roo.View} this
18101 * @param {Number} index The index of the target node
18102 * @param {HTMLElement} node The target node
18103 * @param {Roo.EventObject} e The raw event object
18105 "beforeclick" : true,
18108 * Fires when a template node is clicked.
18109 * @param {Roo.View} this
18110 * @param {Number} index The index of the target node
18111 * @param {HTMLElement} node The target node
18112 * @param {Roo.EventObject} e The raw event object
18117 * Fires when a template node is double clicked.
18118 * @param {Roo.View} this
18119 * @param {Number} index The index of the target node
18120 * @param {HTMLElement} node The target node
18121 * @param {Roo.EventObject} e The raw event object
18125 * @event contextmenu
18126 * Fires when a template node is right clicked.
18127 * @param {Roo.View} this
18128 * @param {Number} index The index of the target node
18129 * @param {HTMLElement} node The target node
18130 * @param {Roo.EventObject} e The raw event object
18132 "contextmenu" : true,
18134 * @event selectionchange
18135 * Fires when the selected nodes change.
18136 * @param {Roo.View} this
18137 * @param {Array} selections Array of the selected nodes
18139 "selectionchange" : true,
18142 * @event beforeselect
18143 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18144 * @param {Roo.View} this
18145 * @param {HTMLElement} node The node to be selected
18146 * @param {Array} selections Array of currently selected nodes
18148 "beforeselect" : true,
18150 * @event preparedata
18151 * Fires on every row to render, to allow you to change the data.
18152 * @param {Roo.View} this
18153 * @param {Object} data to be rendered (change this)
18155 "preparedata" : true
18163 "click": this.onClick,
18164 "dblclick": this.onDblClick,
18165 "contextmenu": this.onContextMenu,
18169 this.selections = [];
18171 this.cmp = new Roo.CompositeElementLite([]);
18173 this.store = Roo.factory(this.store, Roo.data);
18174 this.setStore(this.store, true);
18177 if ( this.footer && this.footer.xtype) {
18179 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18181 this.footer.dataSource = this.store;
18182 this.footer.container = fctr;
18183 this.footer = Roo.factory(this.footer, Roo);
18184 fctr.insertFirst(this.el);
18186 // this is a bit insane - as the paging toolbar seems to detach the el..
18187 // dom.parentNode.parentNode.parentNode
18188 // they get detached?
18192 Roo.View.superclass.constructor.call(this);
18197 Roo.extend(Roo.View, Roo.util.Observable, {
18200 * @cfg {Roo.data.Store} store Data store to load data from.
18205 * @cfg {String|Roo.Element} el The container element.
18210 * @cfg {String|Roo.Template} tpl The template used by this View
18214 * @cfg {String} dataName the named area of the template to use as the data area
18215 * Works with domtemplates roo-name="name"
18219 * @cfg {String} selectedClass The css class to add to selected nodes
18221 selectedClass : "x-view-selected",
18223 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18228 * @cfg {String} text to display on mask (default Loading)
18232 * @cfg {Boolean} multiSelect Allow multiple selection
18234 multiSelect : false,
18236 * @cfg {Boolean} singleSelect Allow single selection
18238 singleSelect: false,
18241 * @cfg {Boolean} toggleSelect - selecting
18243 toggleSelect : false,
18246 * @cfg {Boolean} tickable - selecting
18251 * Returns the element this view is bound to.
18252 * @return {Roo.Element}
18254 getEl : function(){
18255 return this.wrapEl;
18261 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18263 refresh : function(){
18264 //Roo.log('refresh');
18267 // if we are using something like 'domtemplate', then
18268 // the what gets used is:
18269 // t.applySubtemplate(NAME, data, wrapping data..)
18270 // the outer template then get' applied with
18271 // the store 'extra data'
18272 // and the body get's added to the
18273 // roo-name="data" node?
18274 // <span class='roo-tpl-{name}'></span> ?????
18278 this.clearSelections();
18279 this.el.update("");
18281 var records = this.store.getRange();
18282 if(records.length < 1) {
18284 // is this valid?? = should it render a template??
18286 this.el.update(this.emptyText);
18290 if (this.dataName) {
18291 this.el.update(t.apply(this.store.meta)); //????
18292 el = this.el.child('.roo-tpl-' + this.dataName);
18295 for(var i = 0, len = records.length; i < len; i++){
18296 var data = this.prepareData(records[i].data, i, records[i]);
18297 this.fireEvent("preparedata", this, data, i, records[i]);
18299 var d = Roo.apply({}, data);
18302 Roo.apply(d, {'roo-id' : Roo.id()});
18306 Roo.each(this.parent.item, function(item){
18307 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18310 Roo.apply(d, {'roo-data-checked' : 'checked'});
18314 html[html.length] = Roo.util.Format.trim(
18316 t.applySubtemplate(this.dataName, d, this.store.meta) :
18323 el.update(html.join(""));
18324 this.nodes = el.dom.childNodes;
18325 this.updateIndexes(0);
18330 * Function to override to reformat the data that is sent to
18331 * the template for each node.
18332 * DEPRICATED - use the preparedata event handler.
18333 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18334 * a JSON object for an UpdateManager bound view).
18336 prepareData : function(data, index, record)
18338 this.fireEvent("preparedata", this, data, index, record);
18342 onUpdate : function(ds, record){
18343 // Roo.log('on update');
18344 this.clearSelections();
18345 var index = this.store.indexOf(record);
18346 var n = this.nodes[index];
18347 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18348 n.parentNode.removeChild(n);
18349 this.updateIndexes(index, index);
18355 onAdd : function(ds, records, index)
18357 //Roo.log(['on Add', ds, records, index] );
18358 this.clearSelections();
18359 if(this.nodes.length == 0){
18363 var n = this.nodes[index];
18364 for(var i = 0, len = records.length; i < len; i++){
18365 var d = this.prepareData(records[i].data, i, records[i]);
18367 this.tpl.insertBefore(n, d);
18370 this.tpl.append(this.el, d);
18373 this.updateIndexes(index);
18376 onRemove : function(ds, record, index){
18377 // Roo.log('onRemove');
18378 this.clearSelections();
18379 var el = this.dataName ?
18380 this.el.child('.roo-tpl-' + this.dataName) :
18383 el.dom.removeChild(this.nodes[index]);
18384 this.updateIndexes(index);
18388 * Refresh an individual node.
18389 * @param {Number} index
18391 refreshNode : function(index){
18392 this.onUpdate(this.store, this.store.getAt(index));
18395 updateIndexes : function(startIndex, endIndex){
18396 var ns = this.nodes;
18397 startIndex = startIndex || 0;
18398 endIndex = endIndex || ns.length - 1;
18399 for(var i = startIndex; i <= endIndex; i++){
18400 ns[i].nodeIndex = i;
18405 * Changes the data store this view uses and refresh the view.
18406 * @param {Store} store
18408 setStore : function(store, initial){
18409 if(!initial && this.store){
18410 this.store.un("datachanged", this.refresh);
18411 this.store.un("add", this.onAdd);
18412 this.store.un("remove", this.onRemove);
18413 this.store.un("update", this.onUpdate);
18414 this.store.un("clear", this.refresh);
18415 this.store.un("beforeload", this.onBeforeLoad);
18416 this.store.un("load", this.onLoad);
18417 this.store.un("loadexception", this.onLoad);
18421 store.on("datachanged", this.refresh, this);
18422 store.on("add", this.onAdd, this);
18423 store.on("remove", this.onRemove, this);
18424 store.on("update", this.onUpdate, this);
18425 store.on("clear", this.refresh, this);
18426 store.on("beforeload", this.onBeforeLoad, this);
18427 store.on("load", this.onLoad, this);
18428 store.on("loadexception", this.onLoad, this);
18436 * onbeforeLoad - masks the loading area.
18439 onBeforeLoad : function(store,opts)
18441 //Roo.log('onBeforeLoad');
18443 this.el.update("");
18445 this.el.mask(this.mask ? this.mask : "Loading" );
18447 onLoad : function ()
18454 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18455 * @param {HTMLElement} node
18456 * @return {HTMLElement} The template node
18458 findItemFromChild : function(node){
18459 var el = this.dataName ?
18460 this.el.child('.roo-tpl-' + this.dataName,true) :
18463 if(!node || node.parentNode == el){
18466 var p = node.parentNode;
18467 while(p && p != el){
18468 if(p.parentNode == el){
18477 onClick : function(e){
18478 var item = this.findItemFromChild(e.getTarget());
18480 var index = this.indexOf(item);
18481 if(this.onItemClick(item, index, e) !== false){
18482 this.fireEvent("click", this, index, item, e);
18485 this.clearSelections();
18490 onContextMenu : function(e){
18491 var item = this.findItemFromChild(e.getTarget());
18493 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18498 onDblClick : function(e){
18499 var item = this.findItemFromChild(e.getTarget());
18501 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18505 onItemClick : function(item, index, e)
18507 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18510 if (this.toggleSelect) {
18511 var m = this.isSelected(item) ? 'unselect' : 'select';
18514 _t[m](item, true, false);
18517 if(this.multiSelect || this.singleSelect){
18518 if(this.multiSelect && e.shiftKey && this.lastSelection){
18519 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18521 this.select(item, this.multiSelect && e.ctrlKey);
18522 this.lastSelection = item;
18525 if(!this.tickable){
18526 e.preventDefault();
18534 * Get the number of selected nodes.
18537 getSelectionCount : function(){
18538 return this.selections.length;
18542 * Get the currently selected nodes.
18543 * @return {Array} An array of HTMLElements
18545 getSelectedNodes : function(){
18546 return this.selections;
18550 * Get the indexes of the selected nodes.
18553 getSelectedIndexes : function(){
18554 var indexes = [], s = this.selections;
18555 for(var i = 0, len = s.length; i < len; i++){
18556 indexes.push(s[i].nodeIndex);
18562 * Clear all selections
18563 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18565 clearSelections : function(suppressEvent){
18566 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18567 this.cmp.elements = this.selections;
18568 this.cmp.removeClass(this.selectedClass);
18569 this.selections = [];
18570 if(!suppressEvent){
18571 this.fireEvent("selectionchange", this, this.selections);
18577 * Returns true if the passed node is selected
18578 * @param {HTMLElement/Number} node The node or node index
18579 * @return {Boolean}
18581 isSelected : function(node){
18582 var s = this.selections;
18586 node = this.getNode(node);
18587 return s.indexOf(node) !== -1;
18592 * @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
18593 * @param {Boolean} keepExisting (optional) true to keep existing selections
18594 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18596 select : function(nodeInfo, keepExisting, suppressEvent){
18597 if(nodeInfo instanceof Array){
18599 this.clearSelections(true);
18601 for(var i = 0, len = nodeInfo.length; i < len; i++){
18602 this.select(nodeInfo[i], true, true);
18606 var node = this.getNode(nodeInfo);
18607 if(!node || this.isSelected(node)){
18608 return; // already selected.
18611 this.clearSelections(true);
18614 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18615 Roo.fly(node).addClass(this.selectedClass);
18616 this.selections.push(node);
18617 if(!suppressEvent){
18618 this.fireEvent("selectionchange", this, this.selections);
18626 * @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
18627 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18628 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18630 unselect : function(nodeInfo, keepExisting, suppressEvent)
18632 if(nodeInfo instanceof Array){
18633 Roo.each(this.selections, function(s) {
18634 this.unselect(s, nodeInfo);
18638 var node = this.getNode(nodeInfo);
18639 if(!node || !this.isSelected(node)){
18640 //Roo.log("not selected");
18641 return; // not selected.
18645 Roo.each(this.selections, function(s) {
18647 Roo.fly(node).removeClass(this.selectedClass);
18654 this.selections= ns;
18655 this.fireEvent("selectionchange", this, this.selections);
18659 * Gets a template node.
18660 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18661 * @return {HTMLElement} The node or null if it wasn't found
18663 getNode : function(nodeInfo){
18664 if(typeof nodeInfo == "string"){
18665 return document.getElementById(nodeInfo);
18666 }else if(typeof nodeInfo == "number"){
18667 return this.nodes[nodeInfo];
18673 * Gets a range template nodes.
18674 * @param {Number} startIndex
18675 * @param {Number} endIndex
18676 * @return {Array} An array of nodes
18678 getNodes : function(start, end){
18679 var ns = this.nodes;
18680 start = start || 0;
18681 end = typeof end == "undefined" ? ns.length - 1 : end;
18684 for(var i = start; i <= end; i++){
18688 for(var i = start; i >= end; i--){
18696 * Finds the index of the passed node
18697 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18698 * @return {Number} The index of the node or -1
18700 indexOf : function(node){
18701 node = this.getNode(node);
18702 if(typeof node.nodeIndex == "number"){
18703 return node.nodeIndex;
18705 var ns = this.nodes;
18706 for(var i = 0, len = ns.length; i < len; i++){
18717 * based on jquery fullcalendar
18721 Roo.bootstrap = Roo.bootstrap || {};
18723 * @class Roo.bootstrap.Calendar
18724 * @extends Roo.bootstrap.Component
18725 * Bootstrap Calendar class
18726 * @cfg {Boolean} loadMask (true|false) default false
18727 * @cfg {Object} header generate the user specific header of the calendar, default false
18730 * Create a new Container
18731 * @param {Object} config The config object
18736 Roo.bootstrap.Calendar = function(config){
18737 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18741 * Fires when a date is selected
18742 * @param {DatePicker} this
18743 * @param {Date} date The selected date
18747 * @event monthchange
18748 * Fires when the displayed month changes
18749 * @param {DatePicker} this
18750 * @param {Date} date The selected month
18752 'monthchange': true,
18754 * @event evententer
18755 * Fires when mouse over an event
18756 * @param {Calendar} this
18757 * @param {event} Event
18759 'evententer': true,
18761 * @event eventleave
18762 * Fires when the mouse leaves an
18763 * @param {Calendar} this
18766 'eventleave': true,
18768 * @event eventclick
18769 * Fires when the mouse click an
18770 * @param {Calendar} this
18779 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18782 * @cfg {Number} startDay
18783 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18791 getAutoCreate : function(){
18794 var fc_button = function(name, corner, style, content ) {
18795 return Roo.apply({},{
18797 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18799 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18802 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18813 style : 'width:100%',
18820 cls : 'fc-header-left',
18822 fc_button('prev', 'left', 'arrow', '‹' ),
18823 fc_button('next', 'right', 'arrow', '›' ),
18824 { tag: 'span', cls: 'fc-header-space' },
18825 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18833 cls : 'fc-header-center',
18837 cls: 'fc-header-title',
18840 html : 'month / year'
18848 cls : 'fc-header-right',
18850 /* fc_button('month', 'left', '', 'month' ),
18851 fc_button('week', '', '', 'week' ),
18852 fc_button('day', 'right', '', 'day' )
18864 header = this.header;
18867 var cal_heads = function() {
18869 // fixme - handle this.
18871 for (var i =0; i < Date.dayNames.length; i++) {
18872 var d = Date.dayNames[i];
18875 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18876 html : d.substring(0,3)
18880 ret[0].cls += ' fc-first';
18881 ret[6].cls += ' fc-last';
18884 var cal_cell = function(n) {
18887 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18892 cls: 'fc-day-number',
18896 cls: 'fc-day-content',
18900 style: 'position: relative;' // height: 17px;
18912 var cal_rows = function() {
18915 for (var r = 0; r < 6; r++) {
18922 for (var i =0; i < Date.dayNames.length; i++) {
18923 var d = Date.dayNames[i];
18924 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18927 row.cn[0].cls+=' fc-first';
18928 row.cn[0].cn[0].style = 'min-height:90px';
18929 row.cn[6].cls+=' fc-last';
18933 ret[0].cls += ' fc-first';
18934 ret[4].cls += ' fc-prev-last';
18935 ret[5].cls += ' fc-last';
18942 cls: 'fc-border-separate',
18943 style : 'width:100%',
18951 cls : 'fc-first fc-last',
18969 cls : 'fc-content',
18970 style : "position: relative;",
18973 cls : 'fc-view fc-view-month fc-grid',
18974 style : 'position: relative',
18975 unselectable : 'on',
18978 cls : 'fc-event-container',
18979 style : 'position:absolute;z-index:8;top:0;left:0;'
18997 initEvents : function()
19000 throw "can not find store for calendar";
19006 style: "text-align:center",
19010 style: "background-color:white;width:50%;margin:250 auto",
19014 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19025 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19027 var size = this.el.select('.fc-content', true).first().getSize();
19028 this.maskEl.setSize(size.width, size.height);
19029 this.maskEl.enableDisplayMode("block");
19030 if(!this.loadMask){
19031 this.maskEl.hide();
19034 this.store = Roo.factory(this.store, Roo.data);
19035 this.store.on('load', this.onLoad, this);
19036 this.store.on('beforeload', this.onBeforeLoad, this);
19040 this.cells = this.el.select('.fc-day',true);
19041 //Roo.log(this.cells);
19042 this.textNodes = this.el.query('.fc-day-number');
19043 this.cells.addClassOnOver('fc-state-hover');
19045 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19046 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19047 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19048 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19050 this.on('monthchange', this.onMonthChange, this);
19052 this.update(new Date().clearTime());
19055 resize : function() {
19056 var sz = this.el.getSize();
19058 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19059 this.el.select('.fc-day-content div',true).setHeight(34);
19064 showPrevMonth : function(e){
19065 this.update(this.activeDate.add("mo", -1));
19067 showToday : function(e){
19068 this.update(new Date().clearTime());
19071 showNextMonth : function(e){
19072 this.update(this.activeDate.add("mo", 1));
19076 showPrevYear : function(){
19077 this.update(this.activeDate.add("y", -1));
19081 showNextYear : function(){
19082 this.update(this.activeDate.add("y", 1));
19087 update : function(date)
19089 var vd = this.activeDate;
19090 this.activeDate = date;
19091 // if(vd && this.el){
19092 // var t = date.getTime();
19093 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19094 // Roo.log('using add remove');
19096 // this.fireEvent('monthchange', this, date);
19098 // this.cells.removeClass("fc-state-highlight");
19099 // this.cells.each(function(c){
19100 // if(c.dateValue == t){
19101 // c.addClass("fc-state-highlight");
19102 // setTimeout(function(){
19103 // try{c.dom.firstChild.focus();}catch(e){}
19113 var days = date.getDaysInMonth();
19115 var firstOfMonth = date.getFirstDateOfMonth();
19116 var startingPos = firstOfMonth.getDay()-this.startDay;
19118 if(startingPos < this.startDay){
19122 var pm = date.add(Date.MONTH, -1);
19123 var prevStart = pm.getDaysInMonth()-startingPos;
19125 this.cells = this.el.select('.fc-day',true);
19126 this.textNodes = this.el.query('.fc-day-number');
19127 this.cells.addClassOnOver('fc-state-hover');
19129 var cells = this.cells.elements;
19130 var textEls = this.textNodes;
19132 Roo.each(cells, function(cell){
19133 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19136 days += startingPos;
19138 // convert everything to numbers so it's fast
19139 var day = 86400000;
19140 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19143 //Roo.log(prevStart);
19145 var today = new Date().clearTime().getTime();
19146 var sel = date.clearTime().getTime();
19147 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19148 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19149 var ddMatch = this.disabledDatesRE;
19150 var ddText = this.disabledDatesText;
19151 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19152 var ddaysText = this.disabledDaysText;
19153 var format = this.format;
19155 var setCellClass = function(cal, cell){
19159 //Roo.log('set Cell Class');
19161 var t = d.getTime();
19165 cell.dateValue = t;
19167 cell.className += " fc-today";
19168 cell.className += " fc-state-highlight";
19169 cell.title = cal.todayText;
19172 // disable highlight in other month..
19173 //cell.className += " fc-state-highlight";
19178 cell.className = " fc-state-disabled";
19179 cell.title = cal.minText;
19183 cell.className = " fc-state-disabled";
19184 cell.title = cal.maxText;
19188 if(ddays.indexOf(d.getDay()) != -1){
19189 cell.title = ddaysText;
19190 cell.className = " fc-state-disabled";
19193 if(ddMatch && format){
19194 var fvalue = d.dateFormat(format);
19195 if(ddMatch.test(fvalue)){
19196 cell.title = ddText.replace("%0", fvalue);
19197 cell.className = " fc-state-disabled";
19201 if (!cell.initialClassName) {
19202 cell.initialClassName = cell.dom.className;
19205 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19210 for(; i < startingPos; i++) {
19211 textEls[i].innerHTML = (++prevStart);
19212 d.setDate(d.getDate()+1);
19214 cells[i].className = "fc-past fc-other-month";
19215 setCellClass(this, cells[i]);
19220 for(; i < days; i++){
19221 intDay = i - startingPos + 1;
19222 textEls[i].innerHTML = (intDay);
19223 d.setDate(d.getDate()+1);
19225 cells[i].className = ''; // "x-date-active";
19226 setCellClass(this, cells[i]);
19230 for(; i < 42; i++) {
19231 textEls[i].innerHTML = (++extraDays);
19232 d.setDate(d.getDate()+1);
19234 cells[i].className = "fc-future fc-other-month";
19235 setCellClass(this, cells[i]);
19238 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19240 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19242 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19243 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19245 if(totalRows != 6){
19246 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19247 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19250 this.fireEvent('monthchange', this, date);
19254 if(!this.internalRender){
19255 var main = this.el.dom.firstChild;
19256 var w = main.offsetWidth;
19257 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19258 Roo.fly(main).setWidth(w);
19259 this.internalRender = true;
19260 // opera does not respect the auto grow header center column
19261 // then, after it gets a width opera refuses to recalculate
19262 // without a second pass
19263 if(Roo.isOpera && !this.secondPass){
19264 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19265 this.secondPass = true;
19266 this.update.defer(10, this, [date]);
19273 findCell : function(dt) {
19274 dt = dt.clearTime().getTime();
19276 this.cells.each(function(c){
19277 //Roo.log("check " +c.dateValue + '?=' + dt);
19278 if(c.dateValue == dt){
19288 findCells : function(ev) {
19289 var s = ev.start.clone().clearTime().getTime();
19291 var e= ev.end.clone().clearTime().getTime();
19294 this.cells.each(function(c){
19295 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19297 if(c.dateValue > e){
19300 if(c.dateValue < s){
19309 // findBestRow: function(cells)
19313 // for (var i =0 ; i < cells.length;i++) {
19314 // ret = Math.max(cells[i].rows || 0,ret);
19321 addItem : function(ev)
19323 // look for vertical location slot in
19324 var cells = this.findCells(ev);
19326 // ev.row = this.findBestRow(cells);
19328 // work out the location.
19332 for(var i =0; i < cells.length; i++) {
19334 cells[i].row = cells[0].row;
19337 cells[i].row = cells[i].row + 1;
19347 if (crow.start.getY() == cells[i].getY()) {
19349 crow.end = cells[i];
19366 cells[0].events.push(ev);
19368 this.calevents.push(ev);
19371 clearEvents: function() {
19373 if(!this.calevents){
19377 Roo.each(this.cells.elements, function(c){
19383 Roo.each(this.calevents, function(e) {
19384 Roo.each(e.els, function(el) {
19385 el.un('mouseenter' ,this.onEventEnter, this);
19386 el.un('mouseleave' ,this.onEventLeave, this);
19391 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19397 renderEvents: function()
19401 this.cells.each(function(c) {
19410 if(c.row != c.events.length){
19411 r = 4 - (4 - (c.row - c.events.length));
19414 c.events = ev.slice(0, r);
19415 c.more = ev.slice(r);
19417 if(c.more.length && c.more.length == 1){
19418 c.events.push(c.more.pop());
19421 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19425 this.cells.each(function(c) {
19427 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19430 for (var e = 0; e < c.events.length; e++){
19431 var ev = c.events[e];
19432 var rows = ev.rows;
19434 for(var i = 0; i < rows.length; i++) {
19436 // how many rows should it span..
19439 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19440 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19442 unselectable : "on",
19445 cls: 'fc-event-inner',
19449 // cls: 'fc-event-time',
19450 // html : cells.length > 1 ? '' : ev.time
19454 cls: 'fc-event-title',
19455 html : String.format('{0}', ev.title)
19462 cls: 'ui-resizable-handle ui-resizable-e',
19463 html : '  '
19470 cfg.cls += ' fc-event-start';
19472 if ((i+1) == rows.length) {
19473 cfg.cls += ' fc-event-end';
19476 var ctr = _this.el.select('.fc-event-container',true).first();
19477 var cg = ctr.createChild(cfg);
19479 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19480 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19482 var r = (c.more.length) ? 1 : 0;
19483 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19484 cg.setWidth(ebox.right - sbox.x -2);
19486 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19487 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19488 cg.on('click', _this.onEventClick, _this, ev);
19499 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19500 style : 'position: absolute',
19501 unselectable : "on",
19504 cls: 'fc-event-inner',
19508 cls: 'fc-event-title',
19516 cls: 'ui-resizable-handle ui-resizable-e',
19517 html : '  '
19523 var ctr = _this.el.select('.fc-event-container',true).first();
19524 var cg = ctr.createChild(cfg);
19526 var sbox = c.select('.fc-day-content',true).first().getBox();
19527 var ebox = c.select('.fc-day-content',true).first().getBox();
19529 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19530 cg.setWidth(ebox.right - sbox.x -2);
19532 cg.on('click', _this.onMoreEventClick, _this, c.more);
19542 onEventEnter: function (e, el,event,d) {
19543 this.fireEvent('evententer', this, el, event);
19546 onEventLeave: function (e, el,event,d) {
19547 this.fireEvent('eventleave', this, el, event);
19550 onEventClick: function (e, el,event,d) {
19551 this.fireEvent('eventclick', this, el, event);
19554 onMonthChange: function () {
19558 onMoreEventClick: function(e, el, more)
19562 this.calpopover.placement = 'right';
19563 this.calpopover.setTitle('More');
19565 this.calpopover.setContent('');
19567 var ctr = this.calpopover.el.select('.popover-content', true).first();
19569 Roo.each(more, function(m){
19571 cls : 'fc-event-hori fc-event-draggable',
19574 var cg = ctr.createChild(cfg);
19576 cg.on('click', _this.onEventClick, _this, m);
19579 this.calpopover.show(el);
19584 onLoad: function ()
19586 this.calevents = [];
19589 if(this.store.getCount() > 0){
19590 this.store.data.each(function(d){
19593 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19594 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19595 time : d.data.start_time,
19596 title : d.data.title,
19597 description : d.data.description,
19598 venue : d.data.venue
19603 this.renderEvents();
19605 if(this.calevents.length && this.loadMask){
19606 this.maskEl.hide();
19610 onBeforeLoad: function()
19612 this.clearEvents();
19614 this.maskEl.show();
19628 * @class Roo.bootstrap.Popover
19629 * @extends Roo.bootstrap.Component
19630 * Bootstrap Popover class
19631 * @cfg {String} html contents of the popover (or false to use children..)
19632 * @cfg {String} title of popover (or false to hide)
19633 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19634 * @cfg {String} trigger click || hover (or false to trigger manually)
19635 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19636 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19637 * - if false and it has a 'parent' then it will be automatically added to that element
19638 * - if string - Roo.get will be called
19639 * @cfg {Number} delay - delay before showing
19642 * Create a new Popover
19643 * @param {Object} config The config object
19646 Roo.bootstrap.Popover = function(config){
19647 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19653 * After the popover show
19655 * @param {Roo.bootstrap.Popover} this
19660 * After the popover hide
19662 * @param {Roo.bootstrap.Popover} this
19668 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19673 placement : 'right',
19674 trigger : 'hover', // hover
19680 can_build_overlaid : false,
19682 maskEl : false, // the mask element
19685 alignEl : false, // when show is called with an element - this get's stored.
19687 getChildContainer : function()
19689 return this.contentEl;
19692 getPopoverHeader : function()
19694 this.title = true; // flag not to hide it..
19695 this.headerEl.addClass('p-0');
19696 return this.headerEl
19700 getAutoCreate : function(){
19703 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19704 style: 'display:block',
19710 cls : 'popover-inner ',
19714 cls: 'popover-title popover-header',
19715 html : this.title === false ? '' : this.title
19718 cls : 'popover-content popover-body ' + (this.cls || ''),
19719 html : this.html || ''
19730 * @param {string} the title
19732 setTitle: function(str)
19736 this.headerEl.dom.innerHTML = str;
19741 * @param {string} the body content
19743 setContent: function(str)
19746 if (this.contentEl) {
19747 this.contentEl.dom.innerHTML = str;
19751 // as it get's added to the bottom of the page.
19752 onRender : function(ct, position)
19754 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19759 var cfg = Roo.apply({}, this.getAutoCreate());
19763 cfg.cls += ' ' + this.cls;
19766 cfg.style = this.style;
19768 //Roo.log("adding to ");
19769 this.el = Roo.get(document.body).createChild(cfg, position);
19770 // Roo.log(this.el);
19773 this.contentEl = this.el.select('.popover-content',true).first();
19774 this.headerEl = this.el.select('.popover-title',true).first();
19777 if(typeof(this.items) != 'undefined'){
19778 var items = this.items;
19781 for(var i =0;i < items.length;i++) {
19782 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19786 this.items = nitems;
19788 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19789 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19796 resizeMask : function()
19798 this.maskEl.setSize(
19799 Roo.lib.Dom.getViewWidth(true),
19800 Roo.lib.Dom.getViewHeight(true)
19804 initEvents : function()
19808 Roo.bootstrap.Popover.register(this);
19811 this.arrowEl = this.el.select('.arrow',true).first();
19812 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19813 this.el.enableDisplayMode('block');
19817 if (this.over === false && !this.parent()) {
19820 if (this.triggers === false) {
19825 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19826 var triggers = this.trigger ? this.trigger.split(' ') : [];
19827 Roo.each(triggers, function(trigger) {
19829 if (trigger == 'click') {
19830 on_el.on('click', this.toggle, this);
19831 } else if (trigger != 'manual') {
19832 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19833 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19835 on_el.on(eventIn ,this.enter, this);
19836 on_el.on(eventOut, this.leave, this);
19846 toggle : function () {
19847 this.hoverState == 'in' ? this.leave() : this.enter();
19850 enter : function () {
19852 clearTimeout(this.timeout);
19854 this.hoverState = 'in';
19856 if (!this.delay || !this.delay.show) {
19861 this.timeout = setTimeout(function () {
19862 if (_t.hoverState == 'in') {
19865 }, this.delay.show)
19868 leave : function() {
19869 clearTimeout(this.timeout);
19871 this.hoverState = 'out';
19873 if (!this.delay || !this.delay.hide) {
19878 this.timeout = setTimeout(function () {
19879 if (_t.hoverState == 'out') {
19882 }, this.delay.hide)
19886 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19887 * @param {string} (left|right|top|bottom) position
19889 show : function (on_el, placement)
19891 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19892 on_el = on_el || false; // default to false
19895 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19896 on_el = this.parent().el;
19897 } else if (this.over) {
19898 Roo.get(this.over);
19903 this.alignEl = Roo.get( on_el );
19906 this.render(document.body);
19912 if (this.title === false) {
19913 this.headerEl.hide();
19918 this.el.dom.style.display = 'block';
19921 if (this.alignEl) {
19922 this.updatePosition(this.placement, true);
19925 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19926 var es = this.el.getSize();
19927 var x = Roo.lib.Dom.getViewWidth()/2;
19928 var y = Roo.lib.Dom.getViewHeight()/2;
19929 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19934 //var arrow = this.el.select('.arrow',true).first();
19935 //arrow.set(align[2],
19937 this.el.addClass('in');
19941 this.hoverState = 'in';
19944 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19945 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19946 this.maskEl.dom.style.display = 'block';
19947 this.maskEl.addClass('show');
19949 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19951 this.fireEvent('show', this);
19955 * fire this manually after loading a grid in the table for example
19956 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19957 * @param {Boolean} try and move it if we cant get right position.
19959 updatePosition : function(placement, try_move)
19961 // allow for calling with no parameters
19962 placement = placement ? placement : this.placement;
19963 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19965 this.el.removeClass([
19966 'fade','top','bottom', 'left', 'right','in',
19967 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19969 this.el.addClass(placement + ' bs-popover-' + placement);
19971 if (!this.alignEl ) {
19975 switch (placement) {
19977 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19978 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19979 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19980 //normal display... or moved up/down.
19981 this.el.setXY(offset);
19982 var xy = this.alignEl.getAnchorXY('tr', false);
19984 this.arrowEl.setXY(xy);
19987 // continue through...
19988 return this.updatePosition('left', false);
19992 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19993 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19994 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19995 //normal display... or moved up/down.
19996 this.el.setXY(offset);
19997 var xy = this.alignEl.getAnchorXY('tl', false);
19998 xy[0]-=10;xy[1]+=5; // << fix me
19999 this.arrowEl.setXY(xy);
20003 return this.updatePosition('right', false);
20006 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20007 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20008 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20009 //normal display... or moved up/down.
20010 this.el.setXY(offset);
20011 var xy = this.alignEl.getAnchorXY('t', false);
20012 xy[1]-=10; // << fix me
20013 this.arrowEl.setXY(xy);
20017 return this.updatePosition('bottom', false);
20020 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20021 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20022 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20023 //normal display... or moved up/down.
20024 this.el.setXY(offset);
20025 var xy = this.alignEl.getAnchorXY('b', false);
20026 xy[1]+=2; // << fix me
20027 this.arrowEl.setXY(xy);
20031 return this.updatePosition('top', false);
20042 this.el.setXY([0,0]);
20043 this.el.removeClass('in');
20045 this.hoverState = null;
20046 this.maskEl.hide(); // always..
20047 this.fireEvent('hide', this);
20053 Roo.apply(Roo.bootstrap.Popover, {
20056 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20057 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20058 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20059 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20064 clickHander : false,
20067 onMouseDown : function(e)
20069 if (!e.getTarget(".roo-popover")) {
20077 register : function(popup)
20079 if (!Roo.bootstrap.Popover.clickHandler) {
20080 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20082 // hide other popups.
20084 this.popups.push(popup);
20086 hideAll : function()
20088 this.popups.forEach(function(p) {
20096 * Card header - holder for the card header elements.
20101 * @class Roo.bootstrap.PopoverNav
20102 * @extends Roo.bootstrap.NavGroup
20103 * Bootstrap Popover header navigation class
20105 * Create a new Popover Header Navigation
20106 * @param {Object} config The config object
20109 Roo.bootstrap.PopoverNav = function(config){
20110 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20113 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20116 container_method : 'getPopoverHeader'
20134 * @class Roo.bootstrap.Progress
20135 * @extends Roo.bootstrap.Component
20136 * Bootstrap Progress class
20137 * @cfg {Boolean} striped striped of the progress bar
20138 * @cfg {Boolean} active animated of the progress bar
20142 * Create a new Progress
20143 * @param {Object} config The config object
20146 Roo.bootstrap.Progress = function(config){
20147 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20150 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20155 getAutoCreate : function(){
20163 cfg.cls += ' progress-striped';
20167 cfg.cls += ' active';
20186 * @class Roo.bootstrap.ProgressBar
20187 * @extends Roo.bootstrap.Component
20188 * Bootstrap ProgressBar class
20189 * @cfg {Number} aria_valuenow aria-value now
20190 * @cfg {Number} aria_valuemin aria-value min
20191 * @cfg {Number} aria_valuemax aria-value max
20192 * @cfg {String} label label for the progress bar
20193 * @cfg {String} panel (success | info | warning | danger )
20194 * @cfg {String} role role of the progress bar
20195 * @cfg {String} sr_only text
20199 * Create a new ProgressBar
20200 * @param {Object} config The config object
20203 Roo.bootstrap.ProgressBar = function(config){
20204 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20207 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20211 aria_valuemax : 100,
20217 getAutoCreate : function()
20222 cls: 'progress-bar',
20223 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20235 cfg.role = this.role;
20238 if(this.aria_valuenow){
20239 cfg['aria-valuenow'] = this.aria_valuenow;
20242 if(this.aria_valuemin){
20243 cfg['aria-valuemin'] = this.aria_valuemin;
20246 if(this.aria_valuemax){
20247 cfg['aria-valuemax'] = this.aria_valuemax;
20250 if(this.label && !this.sr_only){
20251 cfg.html = this.label;
20255 cfg.cls += ' progress-bar-' + this.panel;
20261 update : function(aria_valuenow)
20263 this.aria_valuenow = aria_valuenow;
20265 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20280 * @class Roo.bootstrap.TabGroup
20281 * @extends Roo.bootstrap.Column
20282 * Bootstrap Column class
20283 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20284 * @cfg {Boolean} carousel true to make the group behave like a carousel
20285 * @cfg {Boolean} bullets show bullets for the panels
20286 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20287 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20288 * @cfg {Boolean} showarrow (true|false) show arrow default true
20291 * Create a new TabGroup
20292 * @param {Object} config The config object
20295 Roo.bootstrap.TabGroup = function(config){
20296 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20298 this.navId = Roo.id();
20301 Roo.bootstrap.TabGroup.register(this);
20305 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20308 transition : false,
20313 slideOnTouch : false,
20316 getAutoCreate : function()
20318 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20320 cfg.cls += ' tab-content';
20322 if (this.carousel) {
20323 cfg.cls += ' carousel slide';
20326 cls : 'carousel-inner',
20330 if(this.bullets && !Roo.isTouch){
20333 cls : 'carousel-bullets',
20337 if(this.bullets_cls){
20338 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20345 cfg.cn[0].cn.push(bullets);
20348 if(this.showarrow){
20349 cfg.cn[0].cn.push({
20351 class : 'carousel-arrow',
20355 class : 'carousel-prev',
20359 class : 'fa fa-chevron-left'
20365 class : 'carousel-next',
20369 class : 'fa fa-chevron-right'
20382 initEvents: function()
20384 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20385 // this.el.on("touchstart", this.onTouchStart, this);
20388 if(this.autoslide){
20391 this.slideFn = window.setInterval(function() {
20392 _this.showPanelNext();
20396 if(this.showarrow){
20397 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20398 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20404 // onTouchStart : function(e, el, o)
20406 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20410 // this.showPanelNext();
20414 getChildContainer : function()
20416 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20420 * register a Navigation item
20421 * @param {Roo.bootstrap.NavItem} the navitem to add
20423 register : function(item)
20425 this.tabs.push( item);
20426 item.navId = this.navId; // not really needed..
20431 getActivePanel : function()
20434 Roo.each(this.tabs, function(t) {
20444 getPanelByName : function(n)
20447 Roo.each(this.tabs, function(t) {
20448 if (t.tabId == n) {
20456 indexOfPanel : function(p)
20459 Roo.each(this.tabs, function(t,i) {
20460 if (t.tabId == p.tabId) {
20469 * show a specific panel
20470 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20471 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20473 showPanel : function (pan)
20475 if(this.transition || typeof(pan) == 'undefined'){
20476 Roo.log("waiting for the transitionend");
20480 if (typeof(pan) == 'number') {
20481 pan = this.tabs[pan];
20484 if (typeof(pan) == 'string') {
20485 pan = this.getPanelByName(pan);
20488 var cur = this.getActivePanel();
20491 Roo.log('pan or acitve pan is undefined');
20495 if (pan.tabId == this.getActivePanel().tabId) {
20499 if (false === cur.fireEvent('beforedeactivate')) {
20503 if(this.bullets > 0 && !Roo.isTouch){
20504 this.setActiveBullet(this.indexOfPanel(pan));
20507 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20509 //class="carousel-item carousel-item-next carousel-item-left"
20511 this.transition = true;
20512 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20513 var lr = dir == 'next' ? 'left' : 'right';
20514 pan.el.addClass(dir); // or prev
20515 pan.el.addClass('carousel-item-' + dir); // or prev
20516 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20517 cur.el.addClass(lr); // or right
20518 pan.el.addClass(lr);
20519 cur.el.addClass('carousel-item-' +lr); // or right
20520 pan.el.addClass('carousel-item-' +lr);
20524 cur.el.on('transitionend', function() {
20525 Roo.log("trans end?");
20527 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20528 pan.setActive(true);
20530 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20531 cur.setActive(false);
20533 _this.transition = false;
20535 }, this, { single: true } );
20540 cur.setActive(false);
20541 pan.setActive(true);
20546 showPanelNext : function()
20548 var i = this.indexOfPanel(this.getActivePanel());
20550 if (i >= this.tabs.length - 1 && !this.autoslide) {
20554 if (i >= this.tabs.length - 1 && this.autoslide) {
20558 this.showPanel(this.tabs[i+1]);
20561 showPanelPrev : function()
20563 var i = this.indexOfPanel(this.getActivePanel());
20565 if (i < 1 && !this.autoslide) {
20569 if (i < 1 && this.autoslide) {
20570 i = this.tabs.length;
20573 this.showPanel(this.tabs[i-1]);
20577 addBullet: function()
20579 if(!this.bullets || Roo.isTouch){
20582 var ctr = this.el.select('.carousel-bullets',true).first();
20583 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20584 var bullet = ctr.createChild({
20585 cls : 'bullet bullet-' + i
20586 },ctr.dom.lastChild);
20591 bullet.on('click', (function(e, el, o, ii, t){
20593 e.preventDefault();
20595 this.showPanel(ii);
20597 if(this.autoslide && this.slideFn){
20598 clearInterval(this.slideFn);
20599 this.slideFn = window.setInterval(function() {
20600 _this.showPanelNext();
20604 }).createDelegate(this, [i, bullet], true));
20609 setActiveBullet : function(i)
20615 Roo.each(this.el.select('.bullet', true).elements, function(el){
20616 el.removeClass('selected');
20619 var bullet = this.el.select('.bullet-' + i, true).first();
20625 bullet.addClass('selected');
20636 Roo.apply(Roo.bootstrap.TabGroup, {
20640 * register a Navigation Group
20641 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20643 register : function(navgrp)
20645 this.groups[navgrp.navId] = navgrp;
20649 * fetch a Navigation Group based on the navigation ID
20650 * if one does not exist , it will get created.
20651 * @param {string} the navgroup to add
20652 * @returns {Roo.bootstrap.NavGroup} the navgroup
20654 get: function(navId) {
20655 if (typeof(this.groups[navId]) == 'undefined') {
20656 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20658 return this.groups[navId] ;
20673 * @class Roo.bootstrap.TabPanel
20674 * @extends Roo.bootstrap.Component
20675 * Bootstrap TabPanel class
20676 * @cfg {Boolean} active panel active
20677 * @cfg {String} html panel content
20678 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20679 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20680 * @cfg {String} href click to link..
20681 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20685 * Create a new TabPanel
20686 * @param {Object} config The config object
20689 Roo.bootstrap.TabPanel = function(config){
20690 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20694 * Fires when the active status changes
20695 * @param {Roo.bootstrap.TabPanel} this
20696 * @param {Boolean} state the new state
20701 * @event beforedeactivate
20702 * Fires before a tab is de-activated - can be used to do validation on a form.
20703 * @param {Roo.bootstrap.TabPanel} this
20704 * @return {Boolean} false if there is an error
20707 'beforedeactivate': true
20710 this.tabId = this.tabId || Roo.id();
20714 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20721 touchSlide : false,
20722 getAutoCreate : function(){
20727 // item is needed for carousel - not sure if it has any effect otherwise
20728 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20729 html: this.html || ''
20733 cfg.cls += ' active';
20737 cfg.tabId = this.tabId;
20745 initEvents: function()
20747 var p = this.parent();
20749 this.navId = this.navId || p.navId;
20751 if (typeof(this.navId) != 'undefined') {
20752 // not really needed.. but just in case.. parent should be a NavGroup.
20753 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20757 var i = tg.tabs.length - 1;
20759 if(this.active && tg.bullets > 0 && i < tg.bullets){
20760 tg.setActiveBullet(i);
20764 this.el.on('click', this.onClick, this);
20766 if(Roo.isTouch && this.touchSlide){
20767 this.el.on("touchstart", this.onTouchStart, this);
20768 this.el.on("touchmove", this.onTouchMove, this);
20769 this.el.on("touchend", this.onTouchEnd, this);
20774 onRender : function(ct, position)
20776 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20779 setActive : function(state)
20781 Roo.log("panel - set active " + this.tabId + "=" + state);
20783 this.active = state;
20785 this.el.removeClass('active');
20787 } else if (!this.el.hasClass('active')) {
20788 this.el.addClass('active');
20791 this.fireEvent('changed', this, state);
20794 onClick : function(e)
20796 e.preventDefault();
20798 if(!this.href.length){
20802 window.location.href = this.href;
20811 onTouchStart : function(e)
20813 this.swiping = false;
20815 this.startX = e.browserEvent.touches[0].clientX;
20816 this.startY = e.browserEvent.touches[0].clientY;
20819 onTouchMove : function(e)
20821 this.swiping = true;
20823 this.endX = e.browserEvent.touches[0].clientX;
20824 this.endY = e.browserEvent.touches[0].clientY;
20827 onTouchEnd : function(e)
20834 var tabGroup = this.parent();
20836 if(this.endX > this.startX){ // swiping right
20837 tabGroup.showPanelPrev();
20841 if(this.startX > this.endX){ // swiping left
20842 tabGroup.showPanelNext();
20861 * @class Roo.bootstrap.DateField
20862 * @extends Roo.bootstrap.Input
20863 * Bootstrap DateField class
20864 * @cfg {Number} weekStart default 0
20865 * @cfg {String} viewMode default empty, (months|years)
20866 * @cfg {String} minViewMode default empty, (months|years)
20867 * @cfg {Number} startDate default -Infinity
20868 * @cfg {Number} endDate default Infinity
20869 * @cfg {Boolean} todayHighlight default false
20870 * @cfg {Boolean} todayBtn default false
20871 * @cfg {Boolean} calendarWeeks default false
20872 * @cfg {Object} daysOfWeekDisabled default empty
20873 * @cfg {Boolean} singleMode default false (true | false)
20875 * @cfg {Boolean} keyboardNavigation default true
20876 * @cfg {String} language default en
20879 * Create a new DateField
20880 * @param {Object} config The config object
20883 Roo.bootstrap.DateField = function(config){
20884 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20888 * Fires when this field show.
20889 * @param {Roo.bootstrap.DateField} this
20890 * @param {Mixed} date The date value
20895 * Fires when this field hide.
20896 * @param {Roo.bootstrap.DateField} this
20897 * @param {Mixed} date The date value
20902 * Fires when select a date.
20903 * @param {Roo.bootstrap.DateField} this
20904 * @param {Mixed} date The date value
20908 * @event beforeselect
20909 * Fires when before select a date.
20910 * @param {Roo.bootstrap.DateField} this
20911 * @param {Mixed} date The date value
20913 beforeselect : true
20917 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20920 * @cfg {String} format
20921 * The default date format string which can be overriden for localization support. The format must be
20922 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20926 * @cfg {String} altFormats
20927 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20928 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20930 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20938 todayHighlight : false,
20944 keyboardNavigation: true,
20946 calendarWeeks: false,
20948 startDate: -Infinity,
20952 daysOfWeekDisabled: [],
20956 singleMode : false,
20958 UTCDate: function()
20960 return new Date(Date.UTC.apply(Date, arguments));
20963 UTCToday: function()
20965 var today = new Date();
20966 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20969 getDate: function() {
20970 var d = this.getUTCDate();
20971 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20974 getUTCDate: function() {
20978 setDate: function(d) {
20979 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20982 setUTCDate: function(d) {
20984 this.setValue(this.formatDate(this.date));
20987 onRender: function(ct, position)
20990 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20992 this.language = this.language || 'en';
20993 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20994 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20996 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20997 this.format = this.format || 'm/d/y';
20998 this.isInline = false;
20999 this.isInput = true;
21000 this.component = this.el.select('.add-on', true).first() || false;
21001 this.component = (this.component && this.component.length === 0) ? false : this.component;
21002 this.hasInput = this.component && this.inputEl().length;
21004 if (typeof(this.minViewMode === 'string')) {
21005 switch (this.minViewMode) {
21007 this.minViewMode = 1;
21010 this.minViewMode = 2;
21013 this.minViewMode = 0;
21018 if (typeof(this.viewMode === 'string')) {
21019 switch (this.viewMode) {
21032 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21034 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21036 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21038 this.picker().on('mousedown', this.onMousedown, this);
21039 this.picker().on('click', this.onClick, this);
21041 this.picker().addClass('datepicker-dropdown');
21043 this.startViewMode = this.viewMode;
21045 if(this.singleMode){
21046 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21047 v.setVisibilityMode(Roo.Element.DISPLAY);
21051 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21052 v.setStyle('width', '189px');
21056 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21057 if(!this.calendarWeeks){
21062 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21063 v.attr('colspan', function(i, val){
21064 return parseInt(val) + 1;
21069 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21071 this.setStartDate(this.startDate);
21072 this.setEndDate(this.endDate);
21074 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21081 if(this.isInline) {
21086 picker : function()
21088 return this.pickerEl;
21089 // return this.el.select('.datepicker', true).first();
21092 fillDow: function()
21094 var dowCnt = this.weekStart;
21103 if(this.calendarWeeks){
21111 while (dowCnt < this.weekStart + 7) {
21115 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21119 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21122 fillMonths: function()
21125 var months = this.picker().select('>.datepicker-months td', true).first();
21127 months.dom.innerHTML = '';
21133 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21136 months.createChild(month);
21143 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;
21145 if (this.date < this.startDate) {
21146 this.viewDate = new Date(this.startDate);
21147 } else if (this.date > this.endDate) {
21148 this.viewDate = new Date(this.endDate);
21150 this.viewDate = new Date(this.date);
21158 var d = new Date(this.viewDate),
21159 year = d.getUTCFullYear(),
21160 month = d.getUTCMonth(),
21161 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21162 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21163 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21164 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21165 currentDate = this.date && this.date.valueOf(),
21166 today = this.UTCToday();
21168 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21170 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21172 // this.picker.select('>tfoot th.today').
21173 // .text(dates[this.language].today)
21174 // .toggle(this.todayBtn !== false);
21176 this.updateNavArrows();
21179 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21181 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21183 prevMonth.setUTCDate(day);
21185 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21187 var nextMonth = new Date(prevMonth);
21189 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21191 nextMonth = nextMonth.valueOf();
21193 var fillMonths = false;
21195 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21197 while(prevMonth.valueOf() <= nextMonth) {
21200 if (prevMonth.getUTCDay() === this.weekStart) {
21202 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21210 if(this.calendarWeeks){
21211 // ISO 8601: First week contains first thursday.
21212 // ISO also states week starts on Monday, but we can be more abstract here.
21214 // Start of current week: based on weekstart/current date
21215 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21216 // Thursday of this week
21217 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21218 // First Thursday of year, year from thursday
21219 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21220 // Calendar week: ms between thursdays, div ms per day, div 7 days
21221 calWeek = (th - yth) / 864e5 / 7 + 1;
21223 fillMonths.cn.push({
21231 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21233 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21236 if (this.todayHighlight &&
21237 prevMonth.getUTCFullYear() == today.getFullYear() &&
21238 prevMonth.getUTCMonth() == today.getMonth() &&
21239 prevMonth.getUTCDate() == today.getDate()) {
21240 clsName += ' today';
21243 if (currentDate && prevMonth.valueOf() === currentDate) {
21244 clsName += ' active';
21247 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21248 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21249 clsName += ' disabled';
21252 fillMonths.cn.push({
21254 cls: 'day ' + clsName,
21255 html: prevMonth.getDate()
21258 prevMonth.setDate(prevMonth.getDate()+1);
21261 var currentYear = this.date && this.date.getUTCFullYear();
21262 var currentMonth = this.date && this.date.getUTCMonth();
21264 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21266 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21267 v.removeClass('active');
21269 if(currentYear === year && k === currentMonth){
21270 v.addClass('active');
21273 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21274 v.addClass('disabled');
21280 year = parseInt(year/10, 10) * 10;
21282 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21284 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21287 for (var i = -1; i < 11; i++) {
21288 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21290 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21298 showMode: function(dir)
21301 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21304 Roo.each(this.picker().select('>div',true).elements, function(v){
21305 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21308 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21313 if(this.isInline) {
21317 this.picker().removeClass(['bottom', 'top']);
21319 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21321 * place to the top of element!
21325 this.picker().addClass('top');
21326 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21331 this.picker().addClass('bottom');
21333 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21336 parseDate : function(value)
21338 if(!value || value instanceof Date){
21341 var v = Date.parseDate(value, this.format);
21342 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21343 v = Date.parseDate(value, 'Y-m-d');
21345 if(!v && this.altFormats){
21346 if(!this.altFormatsArray){
21347 this.altFormatsArray = this.altFormats.split("|");
21349 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21350 v = Date.parseDate(value, this.altFormatsArray[i]);
21356 formatDate : function(date, fmt)
21358 return (!date || !(date instanceof Date)) ?
21359 date : date.dateFormat(fmt || this.format);
21362 onFocus : function()
21364 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21368 onBlur : function()
21370 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21372 var d = this.inputEl().getValue();
21379 showPopup : function()
21381 this.picker().show();
21385 this.fireEvent('showpopup', this, this.date);
21388 hidePopup : function()
21390 if(this.isInline) {
21393 this.picker().hide();
21394 this.viewMode = this.startViewMode;
21397 this.fireEvent('hidepopup', this, this.date);
21401 onMousedown: function(e)
21403 e.stopPropagation();
21404 e.preventDefault();
21409 Roo.bootstrap.DateField.superclass.keyup.call(this);
21413 setValue: function(v)
21415 if(this.fireEvent('beforeselect', this, v) !== false){
21416 var d = new Date(this.parseDate(v) ).clearTime();
21418 if(isNaN(d.getTime())){
21419 this.date = this.viewDate = '';
21420 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21424 v = this.formatDate(d);
21426 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21428 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21432 this.fireEvent('select', this, this.date);
21436 getValue: function()
21438 return this.formatDate(this.date);
21441 fireKey: function(e)
21443 if (!this.picker().isVisible()){
21444 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21450 var dateChanged = false,
21452 newDate, newViewDate;
21457 e.preventDefault();
21461 if (!this.keyboardNavigation) {
21464 dir = e.keyCode == 37 ? -1 : 1;
21467 newDate = this.moveYear(this.date, dir);
21468 newViewDate = this.moveYear(this.viewDate, dir);
21469 } else if (e.shiftKey){
21470 newDate = this.moveMonth(this.date, dir);
21471 newViewDate = this.moveMonth(this.viewDate, dir);
21473 newDate = new Date(this.date);
21474 newDate.setUTCDate(this.date.getUTCDate() + dir);
21475 newViewDate = new Date(this.viewDate);
21476 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21478 if (this.dateWithinRange(newDate)){
21479 this.date = newDate;
21480 this.viewDate = newViewDate;
21481 this.setValue(this.formatDate(this.date));
21483 e.preventDefault();
21484 dateChanged = true;
21489 if (!this.keyboardNavigation) {
21492 dir = e.keyCode == 38 ? -1 : 1;
21494 newDate = this.moveYear(this.date, dir);
21495 newViewDate = this.moveYear(this.viewDate, dir);
21496 } else if (e.shiftKey){
21497 newDate = this.moveMonth(this.date, dir);
21498 newViewDate = this.moveMonth(this.viewDate, dir);
21500 newDate = new Date(this.date);
21501 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21502 newViewDate = new Date(this.viewDate);
21503 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21505 if (this.dateWithinRange(newDate)){
21506 this.date = newDate;
21507 this.viewDate = newViewDate;
21508 this.setValue(this.formatDate(this.date));
21510 e.preventDefault();
21511 dateChanged = true;
21515 this.setValue(this.formatDate(this.date));
21517 e.preventDefault();
21520 this.setValue(this.formatDate(this.date));
21534 onClick: function(e)
21536 e.stopPropagation();
21537 e.preventDefault();
21539 var target = e.getTarget();
21541 if(target.nodeName.toLowerCase() === 'i'){
21542 target = Roo.get(target).dom.parentNode;
21545 var nodeName = target.nodeName;
21546 var className = target.className;
21547 var html = target.innerHTML;
21548 //Roo.log(nodeName);
21550 switch(nodeName.toLowerCase()) {
21552 switch(className) {
21558 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21559 switch(this.viewMode){
21561 this.viewDate = this.moveMonth(this.viewDate, dir);
21565 this.viewDate = this.moveYear(this.viewDate, dir);
21571 var date = new Date();
21572 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21574 this.setValue(this.formatDate(this.date));
21581 if (className.indexOf('disabled') < 0) {
21582 this.viewDate.setUTCDate(1);
21583 if (className.indexOf('month') > -1) {
21584 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21586 var year = parseInt(html, 10) || 0;
21587 this.viewDate.setUTCFullYear(year);
21591 if(this.singleMode){
21592 this.setValue(this.formatDate(this.viewDate));
21603 //Roo.log(className);
21604 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21605 var day = parseInt(html, 10) || 1;
21606 var year = (this.viewDate || new Date()).getUTCFullYear(),
21607 month = (this.viewDate || new Date()).getUTCMonth();
21609 if (className.indexOf('old') > -1) {
21616 } else if (className.indexOf('new') > -1) {
21624 //Roo.log([year,month,day]);
21625 this.date = this.UTCDate(year, month, day,0,0,0,0);
21626 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21628 //Roo.log(this.formatDate(this.date));
21629 this.setValue(this.formatDate(this.date));
21636 setStartDate: function(startDate)
21638 this.startDate = startDate || -Infinity;
21639 if (this.startDate !== -Infinity) {
21640 this.startDate = this.parseDate(this.startDate);
21643 this.updateNavArrows();
21646 setEndDate: function(endDate)
21648 this.endDate = endDate || Infinity;
21649 if (this.endDate !== Infinity) {
21650 this.endDate = this.parseDate(this.endDate);
21653 this.updateNavArrows();
21656 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21658 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21659 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21660 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21662 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21663 return parseInt(d, 10);
21666 this.updateNavArrows();
21669 updateNavArrows: function()
21671 if(this.singleMode){
21675 var d = new Date(this.viewDate),
21676 year = d.getUTCFullYear(),
21677 month = d.getUTCMonth();
21679 Roo.each(this.picker().select('.prev', true).elements, function(v){
21681 switch (this.viewMode) {
21684 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21690 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21697 Roo.each(this.picker().select('.next', true).elements, function(v){
21699 switch (this.viewMode) {
21702 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21708 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21716 moveMonth: function(date, dir)
21721 var new_date = new Date(date.valueOf()),
21722 day = new_date.getUTCDate(),
21723 month = new_date.getUTCMonth(),
21724 mag = Math.abs(dir),
21726 dir = dir > 0 ? 1 : -1;
21729 // If going back one month, make sure month is not current month
21730 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21732 return new_date.getUTCMonth() == month;
21734 // If going forward one month, make sure month is as expected
21735 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21737 return new_date.getUTCMonth() != new_month;
21739 new_month = month + dir;
21740 new_date.setUTCMonth(new_month);
21741 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21742 if (new_month < 0 || new_month > 11) {
21743 new_month = (new_month + 12) % 12;
21746 // For magnitudes >1, move one month at a time...
21747 for (var i=0; i<mag; i++) {
21748 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21749 new_date = this.moveMonth(new_date, dir);
21751 // ...then reset the day, keeping it in the new month
21752 new_month = new_date.getUTCMonth();
21753 new_date.setUTCDate(day);
21755 return new_month != new_date.getUTCMonth();
21758 // Common date-resetting loop -- if date is beyond end of month, make it
21761 new_date.setUTCDate(--day);
21762 new_date.setUTCMonth(new_month);
21767 moveYear: function(date, dir)
21769 return this.moveMonth(date, dir*12);
21772 dateWithinRange: function(date)
21774 return date >= this.startDate && date <= this.endDate;
21780 this.picker().remove();
21783 validateValue : function(value)
21785 if(this.getVisibilityEl().hasClass('hidden')){
21789 if(value.length < 1) {
21790 if(this.allowBlank){
21796 if(value.length < this.minLength){
21799 if(value.length > this.maxLength){
21803 var vt = Roo.form.VTypes;
21804 if(!vt[this.vtype](value, this)){
21808 if(typeof this.validator == "function"){
21809 var msg = this.validator(value);
21815 if(this.regex && !this.regex.test(value)){
21819 if(typeof(this.parseDate(value)) == 'undefined'){
21823 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21827 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21837 this.date = this.viewDate = '';
21839 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21844 Roo.apply(Roo.bootstrap.DateField, {
21855 html: '<i class="fa fa-arrow-left"/>'
21865 html: '<i class="fa fa-arrow-right"/>'
21907 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21908 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21909 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21910 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21911 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21924 navFnc: 'FullYear',
21929 navFnc: 'FullYear',
21934 Roo.apply(Roo.bootstrap.DateField, {
21938 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21942 cls: 'datepicker-days',
21946 cls: 'table-condensed',
21948 Roo.bootstrap.DateField.head,
21952 Roo.bootstrap.DateField.footer
21959 cls: 'datepicker-months',
21963 cls: 'table-condensed',
21965 Roo.bootstrap.DateField.head,
21966 Roo.bootstrap.DateField.content,
21967 Roo.bootstrap.DateField.footer
21974 cls: 'datepicker-years',
21978 cls: 'table-condensed',
21980 Roo.bootstrap.DateField.head,
21981 Roo.bootstrap.DateField.content,
21982 Roo.bootstrap.DateField.footer
22001 * @class Roo.bootstrap.TimeField
22002 * @extends Roo.bootstrap.Input
22003 * Bootstrap DateField class
22007 * Create a new TimeField
22008 * @param {Object} config The config object
22011 Roo.bootstrap.TimeField = function(config){
22012 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22016 * Fires when this field show.
22017 * @param {Roo.bootstrap.DateField} thisthis
22018 * @param {Mixed} date The date value
22023 * Fires when this field hide.
22024 * @param {Roo.bootstrap.DateField} this
22025 * @param {Mixed} date The date value
22030 * Fires when select a date.
22031 * @param {Roo.bootstrap.DateField} this
22032 * @param {Mixed} date The date value
22038 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22041 * @cfg {String} format
22042 * The default time format string which can be overriden for localization support. The format must be
22043 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22047 getAutoCreate : function()
22049 this.after = '<i class="fa far fa-clock"></i>';
22050 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22054 onRender: function(ct, position)
22057 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22059 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22061 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22063 this.pop = this.picker().select('>.datepicker-time',true).first();
22064 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22066 this.picker().on('mousedown', this.onMousedown, this);
22067 this.picker().on('click', this.onClick, this);
22069 this.picker().addClass('datepicker-dropdown');
22074 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22075 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22076 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22077 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22078 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22079 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22083 fireKey: function(e){
22084 if (!this.picker().isVisible()){
22085 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22091 e.preventDefault();
22099 this.onTogglePeriod();
22102 this.onIncrementMinutes();
22105 this.onDecrementMinutes();
22114 onClick: function(e) {
22115 e.stopPropagation();
22116 e.preventDefault();
22119 picker : function()
22121 return this.pickerEl;
22124 fillTime: function()
22126 var time = this.pop.select('tbody', true).first();
22128 time.dom.innerHTML = '';
22143 cls: 'hours-up fa fas fa-chevron-up'
22163 cls: 'minutes-up fa fas fa-chevron-up'
22184 cls: 'timepicker-hour',
22199 cls: 'timepicker-minute',
22214 cls: 'btn btn-primary period',
22236 cls: 'hours-down fa fas fa-chevron-down'
22256 cls: 'minutes-down fa fas fa-chevron-down'
22274 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22281 var hours = this.time.getHours();
22282 var minutes = this.time.getMinutes();
22295 hours = hours - 12;
22299 hours = '0' + hours;
22303 minutes = '0' + minutes;
22306 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22307 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22308 this.pop.select('button', true).first().dom.innerHTML = period;
22314 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22316 var cls = ['bottom'];
22318 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22325 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22329 //this.picker().setXY(20000,20000);
22330 this.picker().addClass(cls.join('-'));
22334 Roo.each(cls, function(c){
22339 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22340 //_this.picker().setTop(_this.inputEl().getHeight());
22344 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22346 //_this.picker().setTop(0 - _this.picker().getHeight());
22351 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22355 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22363 onFocus : function()
22365 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22369 onBlur : function()
22371 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22377 this.picker().show();
22382 this.fireEvent('show', this, this.date);
22387 this.picker().hide();
22390 this.fireEvent('hide', this, this.date);
22393 setTime : function()
22396 this.setValue(this.time.format(this.format));
22398 this.fireEvent('select', this, this.date);
22403 onMousedown: function(e){
22404 e.stopPropagation();
22405 e.preventDefault();
22408 onIncrementHours: function()
22410 Roo.log('onIncrementHours');
22411 this.time = this.time.add(Date.HOUR, 1);
22416 onDecrementHours: function()
22418 Roo.log('onDecrementHours');
22419 this.time = this.time.add(Date.HOUR, -1);
22423 onIncrementMinutes: function()
22425 Roo.log('onIncrementMinutes');
22426 this.time = this.time.add(Date.MINUTE, 1);
22430 onDecrementMinutes: function()
22432 Roo.log('onDecrementMinutes');
22433 this.time = this.time.add(Date.MINUTE, -1);
22437 onTogglePeriod: function()
22439 Roo.log('onTogglePeriod');
22440 this.time = this.time.add(Date.HOUR, 12);
22448 Roo.apply(Roo.bootstrap.TimeField, {
22452 cls: 'datepicker dropdown-menu',
22456 cls: 'datepicker-time',
22460 cls: 'table-condensed',
22489 cls: 'btn btn-info ok',
22517 * @class Roo.bootstrap.MonthField
22518 * @extends Roo.bootstrap.Input
22519 * Bootstrap MonthField class
22521 * @cfg {String} language default en
22524 * Create a new MonthField
22525 * @param {Object} config The config object
22528 Roo.bootstrap.MonthField = function(config){
22529 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22534 * Fires when this field show.
22535 * @param {Roo.bootstrap.MonthField} this
22536 * @param {Mixed} date The date value
22541 * Fires when this field hide.
22542 * @param {Roo.bootstrap.MonthField} this
22543 * @param {Mixed} date The date value
22548 * Fires when select a date.
22549 * @param {Roo.bootstrap.MonthField} this
22550 * @param {String} oldvalue The old value
22551 * @param {String} newvalue The new value
22557 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22559 onRender: function(ct, position)
22562 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22564 this.language = this.language || 'en';
22565 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22566 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22568 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22569 this.isInline = false;
22570 this.isInput = true;
22571 this.component = this.el.select('.add-on', true).first() || false;
22572 this.component = (this.component && this.component.length === 0) ? false : this.component;
22573 this.hasInput = this.component && this.inputEL().length;
22575 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22577 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22579 this.picker().on('mousedown', this.onMousedown, this);
22580 this.picker().on('click', this.onClick, this);
22582 this.picker().addClass('datepicker-dropdown');
22584 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22585 v.setStyle('width', '189px');
22592 if(this.isInline) {
22598 setValue: function(v, suppressEvent)
22600 var o = this.getValue();
22602 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22606 if(suppressEvent !== true){
22607 this.fireEvent('select', this, o, v);
22612 getValue: function()
22617 onClick: function(e)
22619 e.stopPropagation();
22620 e.preventDefault();
22622 var target = e.getTarget();
22624 if(target.nodeName.toLowerCase() === 'i'){
22625 target = Roo.get(target).dom.parentNode;
22628 var nodeName = target.nodeName;
22629 var className = target.className;
22630 var html = target.innerHTML;
22632 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22636 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22638 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22644 picker : function()
22646 return this.pickerEl;
22649 fillMonths: function()
22652 var months = this.picker().select('>.datepicker-months td', true).first();
22654 months.dom.innerHTML = '';
22660 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22663 months.createChild(month);
22672 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22673 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22676 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22677 e.removeClass('active');
22679 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22680 e.addClass('active');
22687 if(this.isInline) {
22691 this.picker().removeClass(['bottom', 'top']);
22693 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22695 * place to the top of element!
22699 this.picker().addClass('top');
22700 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22705 this.picker().addClass('bottom');
22707 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22710 onFocus : function()
22712 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22716 onBlur : function()
22718 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22720 var d = this.inputEl().getValue();
22729 this.picker().show();
22730 this.picker().select('>.datepicker-months', true).first().show();
22734 this.fireEvent('show', this, this.date);
22739 if(this.isInline) {
22742 this.picker().hide();
22743 this.fireEvent('hide', this, this.date);
22747 onMousedown: function(e)
22749 e.stopPropagation();
22750 e.preventDefault();
22755 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22759 fireKey: function(e)
22761 if (!this.picker().isVisible()){
22762 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22773 e.preventDefault();
22777 dir = e.keyCode == 37 ? -1 : 1;
22779 this.vIndex = this.vIndex + dir;
22781 if(this.vIndex < 0){
22785 if(this.vIndex > 11){
22789 if(isNaN(this.vIndex)){
22793 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22799 dir = e.keyCode == 38 ? -1 : 1;
22801 this.vIndex = this.vIndex + dir * 4;
22803 if(this.vIndex < 0){
22807 if(this.vIndex > 11){
22811 if(isNaN(this.vIndex)){
22815 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22820 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22821 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22825 e.preventDefault();
22828 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22829 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22845 this.picker().remove();
22850 Roo.apply(Roo.bootstrap.MonthField, {
22869 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22870 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22875 Roo.apply(Roo.bootstrap.MonthField, {
22879 cls: 'datepicker dropdown-menu roo-dynamic',
22883 cls: 'datepicker-months',
22887 cls: 'table-condensed',
22889 Roo.bootstrap.DateField.content
22909 * @class Roo.bootstrap.CheckBox
22910 * @extends Roo.bootstrap.Input
22911 * Bootstrap CheckBox class
22913 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22914 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22915 * @cfg {String} boxLabel The text that appears beside the checkbox
22916 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22917 * @cfg {Boolean} checked initnal the element
22918 * @cfg {Boolean} inline inline the element (default false)
22919 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22920 * @cfg {String} tooltip label tooltip
22923 * Create a new CheckBox
22924 * @param {Object} config The config object
22927 Roo.bootstrap.CheckBox = function(config){
22928 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22933 * Fires when the element is checked or unchecked.
22934 * @param {Roo.bootstrap.CheckBox} this This input
22935 * @param {Boolean} checked The new checked value
22940 * Fires when the element is click.
22941 * @param {Roo.bootstrap.CheckBox} this This input
22948 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22950 inputType: 'checkbox',
22959 // checkbox success does not make any sense really..
22964 getAutoCreate : function()
22966 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22972 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22975 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22981 type : this.inputType,
22982 value : this.inputValue,
22983 cls : 'roo-' + this.inputType, //'form-box',
22984 placeholder : this.placeholder || ''
22988 if(this.inputType != 'radio'){
22992 cls : 'roo-hidden-value',
22993 value : this.checked ? this.inputValue : this.valueOff
22998 if (this.weight) { // Validity check?
22999 cfg.cls += " " + this.inputType + "-" + this.weight;
23002 if (this.disabled) {
23003 input.disabled=true;
23007 input.checked = this.checked;
23012 input.name = this.name;
23014 if(this.inputType != 'radio'){
23015 hidden.name = this.name;
23016 input.name = '_hidden_' + this.name;
23021 input.cls += ' input-' + this.size;
23026 ['xs','sm','md','lg'].map(function(size){
23027 if (settings[size]) {
23028 cfg.cls += ' col-' + size + '-' + settings[size];
23032 var inputblock = input;
23034 if (this.before || this.after) {
23037 cls : 'input-group',
23042 inputblock.cn.push({
23044 cls : 'input-group-addon',
23049 inputblock.cn.push(input);
23051 if(this.inputType != 'radio'){
23052 inputblock.cn.push(hidden);
23056 inputblock.cn.push({
23058 cls : 'input-group-addon',
23064 var boxLabelCfg = false;
23070 //'for': id, // box label is handled by onclick - so no for...
23072 html: this.boxLabel
23075 boxLabelCfg.tooltip = this.tooltip;
23081 if (align ==='left' && this.fieldLabel.length) {
23082 // Roo.log("left and has label");
23087 cls : 'control-label',
23088 html : this.fieldLabel
23099 cfg.cn[1].cn.push(boxLabelCfg);
23102 if(this.labelWidth > 12){
23103 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23106 if(this.labelWidth < 13 && this.labelmd == 0){
23107 this.labelmd = this.labelWidth;
23110 if(this.labellg > 0){
23111 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23112 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23115 if(this.labelmd > 0){
23116 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23117 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23120 if(this.labelsm > 0){
23121 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23122 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23125 if(this.labelxs > 0){
23126 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23127 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23130 } else if ( this.fieldLabel.length) {
23131 // Roo.log(" label");
23135 tag: this.boxLabel ? 'span' : 'label',
23137 cls: 'control-label box-input-label',
23138 //cls : 'input-group-addon',
23139 html : this.fieldLabel
23146 cfg.cn.push(boxLabelCfg);
23151 // Roo.log(" no label && no align");
23152 cfg.cn = [ inputblock ] ;
23154 cfg.cn.push(boxLabelCfg);
23162 if(this.inputType != 'radio'){
23163 cfg.cn.push(hidden);
23171 * return the real input element.
23173 inputEl: function ()
23175 return this.el.select('input.roo-' + this.inputType,true).first();
23177 hiddenEl: function ()
23179 return this.el.select('input.roo-hidden-value',true).first();
23182 labelEl: function()
23184 return this.el.select('label.control-label',true).first();
23186 /* depricated... */
23190 return this.labelEl();
23193 boxLabelEl: function()
23195 return this.el.select('label.box-label',true).first();
23198 initEvents : function()
23200 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23202 this.inputEl().on('click', this.onClick, this);
23204 if (this.boxLabel) {
23205 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23208 this.startValue = this.getValue();
23211 Roo.bootstrap.CheckBox.register(this);
23215 onClick : function(e)
23217 if(this.fireEvent('click', this, e) !== false){
23218 this.setChecked(!this.checked);
23223 setChecked : function(state,suppressEvent)
23225 this.startValue = this.getValue();
23227 if(this.inputType == 'radio'){
23229 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23230 e.dom.checked = false;
23233 this.inputEl().dom.checked = true;
23235 this.inputEl().dom.value = this.inputValue;
23237 if(suppressEvent !== true){
23238 this.fireEvent('check', this, true);
23246 this.checked = state;
23248 this.inputEl().dom.checked = state;
23251 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23253 if(suppressEvent !== true){
23254 this.fireEvent('check', this, state);
23260 getValue : function()
23262 if(this.inputType == 'radio'){
23263 return this.getGroupValue();
23266 return this.hiddenEl().dom.value;
23270 getGroupValue : function()
23272 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23276 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23279 setValue : function(v,suppressEvent)
23281 if(this.inputType == 'radio'){
23282 this.setGroupValue(v, suppressEvent);
23286 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23291 setGroupValue : function(v, suppressEvent)
23293 this.startValue = this.getValue();
23295 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23296 e.dom.checked = false;
23298 if(e.dom.value == v){
23299 e.dom.checked = true;
23303 if(suppressEvent !== true){
23304 this.fireEvent('check', this, true);
23312 validate : function()
23314 if(this.getVisibilityEl().hasClass('hidden')){
23320 (this.inputType == 'radio' && this.validateRadio()) ||
23321 (this.inputType == 'checkbox' && this.validateCheckbox())
23327 this.markInvalid();
23331 validateRadio : function()
23333 if(this.getVisibilityEl().hasClass('hidden')){
23337 if(this.allowBlank){
23343 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23344 if(!e.dom.checked){
23356 validateCheckbox : function()
23359 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23360 //return (this.getValue() == this.inputValue) ? true : false;
23363 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23371 for(var i in group){
23372 if(group[i].el.isVisible(true)){
23380 for(var i in group){
23385 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23392 * Mark this field as valid
23394 markValid : function()
23398 this.fireEvent('valid', this);
23400 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23403 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23410 if(this.inputType == 'radio'){
23411 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23412 var fg = e.findParent('.form-group', false, true);
23413 if (Roo.bootstrap.version == 3) {
23414 fg.removeClass([_this.invalidClass, _this.validClass]);
23415 fg.addClass(_this.validClass);
23417 fg.removeClass(['is-valid', 'is-invalid']);
23418 fg.addClass('is-valid');
23426 var fg = this.el.findParent('.form-group', false, true);
23427 if (Roo.bootstrap.version == 3) {
23428 fg.removeClass([this.invalidClass, this.validClass]);
23429 fg.addClass(this.validClass);
23431 fg.removeClass(['is-valid', 'is-invalid']);
23432 fg.addClass('is-valid');
23437 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23443 for(var i in group){
23444 var fg = group[i].el.findParent('.form-group', false, true);
23445 if (Roo.bootstrap.version == 3) {
23446 fg.removeClass([this.invalidClass, this.validClass]);
23447 fg.addClass(this.validClass);
23449 fg.removeClass(['is-valid', 'is-invalid']);
23450 fg.addClass('is-valid');
23456 * Mark this field as invalid
23457 * @param {String} msg The validation message
23459 markInvalid : function(msg)
23461 if(this.allowBlank){
23467 this.fireEvent('invalid', this, msg);
23469 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23472 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23476 label.markInvalid();
23479 if(this.inputType == 'radio'){
23481 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23482 var fg = e.findParent('.form-group', false, true);
23483 if (Roo.bootstrap.version == 3) {
23484 fg.removeClass([_this.invalidClass, _this.validClass]);
23485 fg.addClass(_this.invalidClass);
23487 fg.removeClass(['is-invalid', 'is-valid']);
23488 fg.addClass('is-invalid');
23496 var fg = this.el.findParent('.form-group', false, true);
23497 if (Roo.bootstrap.version == 3) {
23498 fg.removeClass([_this.invalidClass, _this.validClass]);
23499 fg.addClass(_this.invalidClass);
23501 fg.removeClass(['is-invalid', 'is-valid']);
23502 fg.addClass('is-invalid');
23507 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23513 for(var i in group){
23514 var fg = group[i].el.findParent('.form-group', false, true);
23515 if (Roo.bootstrap.version == 3) {
23516 fg.removeClass([_this.invalidClass, _this.validClass]);
23517 fg.addClass(_this.invalidClass);
23519 fg.removeClass(['is-invalid', 'is-valid']);
23520 fg.addClass('is-invalid');
23526 clearInvalid : function()
23528 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23530 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23532 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23534 if (label && label.iconEl) {
23535 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23536 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23540 disable : function()
23542 if(this.inputType != 'radio'){
23543 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23550 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23551 _this.getActionEl().addClass(this.disabledClass);
23552 e.dom.disabled = true;
23556 this.disabled = true;
23557 this.fireEvent("disable", this);
23561 enable : function()
23563 if(this.inputType != 'radio'){
23564 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23571 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23572 _this.getActionEl().removeClass(this.disabledClass);
23573 e.dom.disabled = false;
23577 this.disabled = false;
23578 this.fireEvent("enable", this);
23582 setBoxLabel : function(v)
23587 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23593 Roo.apply(Roo.bootstrap.CheckBox, {
23598 * register a CheckBox Group
23599 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23601 register : function(checkbox)
23603 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23604 this.groups[checkbox.groupId] = {};
23607 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23611 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23615 * fetch a CheckBox Group based on the group ID
23616 * @param {string} the group ID
23617 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23619 get: function(groupId) {
23620 if (typeof(this.groups[groupId]) == 'undefined') {
23624 return this.groups[groupId] ;
23637 * @class Roo.bootstrap.Radio
23638 * @extends Roo.bootstrap.Component
23639 * Bootstrap Radio class
23640 * @cfg {String} boxLabel - the label associated
23641 * @cfg {String} value - the value of radio
23644 * Create a new Radio
23645 * @param {Object} config The config object
23647 Roo.bootstrap.Radio = function(config){
23648 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23652 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23658 getAutoCreate : function()
23662 cls : 'form-group radio',
23667 html : this.boxLabel
23675 initEvents : function()
23677 this.parent().register(this);
23679 this.el.on('click', this.onClick, this);
23683 onClick : function(e)
23685 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23686 this.setChecked(true);
23690 setChecked : function(state, suppressEvent)
23692 this.parent().setValue(this.value, suppressEvent);
23696 setBoxLabel : function(v)
23701 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23716 * @class Roo.bootstrap.SecurePass
23717 * @extends Roo.bootstrap.Input
23718 * Bootstrap SecurePass class
23722 * Create a new SecurePass
23723 * @param {Object} config The config object
23726 Roo.bootstrap.SecurePass = function (config) {
23727 // these go here, so the translation tool can replace them..
23729 PwdEmpty: "Please type a password, and then retype it to confirm.",
23730 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23731 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23732 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23733 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23734 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23735 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23736 TooWeak: "Your password is Too Weak."
23738 this.meterLabel = "Password strength:";
23739 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23740 this.meterClass = [
23741 "roo-password-meter-tooweak",
23742 "roo-password-meter-weak",
23743 "roo-password-meter-medium",
23744 "roo-password-meter-strong",
23745 "roo-password-meter-grey"
23750 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23753 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23755 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23757 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23758 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23759 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23760 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23761 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23762 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23763 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23773 * @cfg {String/Object} Label for the strength meter (defaults to
23774 * 'Password strength:')
23779 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23780 * ['Weak', 'Medium', 'Strong'])
23783 pwdStrengths: false,
23796 initEvents: function ()
23798 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23800 if (this.el.is('input[type=password]') && Roo.isSafari) {
23801 this.el.on('keydown', this.SafariOnKeyDown, this);
23804 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23807 onRender: function (ct, position)
23809 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23810 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23811 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23813 this.trigger.createChild({
23818 cls: 'roo-password-meter-grey col-xs-12',
23821 //width: this.meterWidth + 'px'
23825 cls: 'roo-password-meter-text'
23831 if (this.hideTrigger) {
23832 this.trigger.setDisplayed(false);
23834 this.setSize(this.width || '', this.height || '');
23837 onDestroy: function ()
23839 if (this.trigger) {
23840 this.trigger.removeAllListeners();
23841 this.trigger.remove();
23844 this.wrap.remove();
23846 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23849 checkStrength: function ()
23851 var pwd = this.inputEl().getValue();
23852 if (pwd == this._lastPwd) {
23857 if (this.ClientSideStrongPassword(pwd)) {
23859 } else if (this.ClientSideMediumPassword(pwd)) {
23861 } else if (this.ClientSideWeakPassword(pwd)) {
23867 Roo.log('strength1: ' + strength);
23869 //var pm = this.trigger.child('div/div/div').dom;
23870 var pm = this.trigger.child('div/div');
23871 pm.removeClass(this.meterClass);
23872 pm.addClass(this.meterClass[strength]);
23875 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23877 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23879 this._lastPwd = pwd;
23883 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23885 this._lastPwd = '';
23887 var pm = this.trigger.child('div/div');
23888 pm.removeClass(this.meterClass);
23889 pm.addClass('roo-password-meter-grey');
23892 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23895 this.inputEl().dom.type='password';
23898 validateValue: function (value)
23900 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23903 if (value.length == 0) {
23904 if (this.allowBlank) {
23905 this.clearInvalid();
23909 this.markInvalid(this.errors.PwdEmpty);
23910 this.errorMsg = this.errors.PwdEmpty;
23918 if (!value.match(/[\x21-\x7e]+/)) {
23919 this.markInvalid(this.errors.PwdBadChar);
23920 this.errorMsg = this.errors.PwdBadChar;
23923 if (value.length < 6) {
23924 this.markInvalid(this.errors.PwdShort);
23925 this.errorMsg = this.errors.PwdShort;
23928 if (value.length > 16) {
23929 this.markInvalid(this.errors.PwdLong);
23930 this.errorMsg = this.errors.PwdLong;
23934 if (this.ClientSideStrongPassword(value)) {
23936 } else if (this.ClientSideMediumPassword(value)) {
23938 } else if (this.ClientSideWeakPassword(value)) {
23945 if (strength < 2) {
23946 //this.markInvalid(this.errors.TooWeak);
23947 this.errorMsg = this.errors.TooWeak;
23952 console.log('strength2: ' + strength);
23954 //var pm = this.trigger.child('div/div/div').dom;
23956 var pm = this.trigger.child('div/div');
23957 pm.removeClass(this.meterClass);
23958 pm.addClass(this.meterClass[strength]);
23960 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23962 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23964 this.errorMsg = '';
23968 CharacterSetChecks: function (type)
23971 this.fResult = false;
23974 isctype: function (character, type)
23977 case this.kCapitalLetter:
23978 if (character >= 'A' && character <= 'Z') {
23983 case this.kSmallLetter:
23984 if (character >= 'a' && character <= 'z') {
23990 if (character >= '0' && character <= '9') {
23995 case this.kPunctuation:
23996 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24007 IsLongEnough: function (pwd, size)
24009 return !(pwd == null || isNaN(size) || pwd.length < size);
24012 SpansEnoughCharacterSets: function (word, nb)
24014 if (!this.IsLongEnough(word, nb))
24019 var characterSetChecks = new Array(
24020 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24021 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24024 for (var index = 0; index < word.length; ++index) {
24025 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24026 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24027 characterSetChecks[nCharSet].fResult = true;
24034 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24035 if (characterSetChecks[nCharSet].fResult) {
24040 if (nCharSets < nb) {
24046 ClientSideStrongPassword: function (pwd)
24048 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24051 ClientSideMediumPassword: function (pwd)
24053 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24056 ClientSideWeakPassword: function (pwd)
24058 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24061 })//<script type="text/javascript">
24064 * Based Ext JS Library 1.1.1
24065 * Copyright(c) 2006-2007, Ext JS, LLC.
24071 * @class Roo.HtmlEditorCore
24072 * @extends Roo.Component
24073 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24075 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24078 Roo.HtmlEditorCore = function(config){
24081 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24086 * @event initialize
24087 * Fires when the editor is fully initialized (including the iframe)
24088 * @param {Roo.HtmlEditorCore} this
24093 * Fires when the editor is first receives the focus. Any insertion must wait
24094 * until after this event.
24095 * @param {Roo.HtmlEditorCore} this
24099 * @event beforesync
24100 * Fires before the textarea is updated with content from the editor iframe. Return false
24101 * to cancel the sync.
24102 * @param {Roo.HtmlEditorCore} this
24103 * @param {String} html
24107 * @event beforepush
24108 * Fires before the iframe editor is updated with content from the textarea. Return false
24109 * to cancel the push.
24110 * @param {Roo.HtmlEditorCore} this
24111 * @param {String} html
24116 * Fires when the textarea is updated with content from the editor iframe.
24117 * @param {Roo.HtmlEditorCore} this
24118 * @param {String} html
24123 * Fires when the iframe editor is updated with content from the textarea.
24124 * @param {Roo.HtmlEditorCore} this
24125 * @param {String} html
24130 * @event editorevent
24131 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24132 * @param {Roo.HtmlEditorCore} this
24138 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24140 // defaults : white / black...
24141 this.applyBlacklists();
24148 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24152 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24158 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24163 * @cfg {Number} height (in pixels)
24167 * @cfg {Number} width (in pixels)
24172 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24175 stylesheets: false,
24180 // private properties
24181 validationEvent : false,
24183 initialized : false,
24185 sourceEditMode : false,
24186 onFocus : Roo.emptyFn,
24188 hideMode:'offsets',
24192 // blacklist + whitelisted elements..
24199 * Protected method that will not generally be called directly. It
24200 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24201 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24203 getDocMarkup : function(){
24207 // inherit styels from page...??
24208 if (this.stylesheets === false) {
24210 Roo.get(document.head).select('style').each(function(node) {
24211 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24214 Roo.get(document.head).select('link').each(function(node) {
24215 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24218 } else if (!this.stylesheets.length) {
24220 st = '<style type="text/css">' +
24221 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24224 for (var i in this.stylesheets) {
24225 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24230 st += '<style type="text/css">' +
24231 'IMG { cursor: pointer } ' +
24234 var cls = 'roo-htmleditor-body';
24236 if(this.bodyCls.length){
24237 cls += ' ' + this.bodyCls;
24240 return '<html><head>' + st +
24241 //<style type="text/css">' +
24242 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24244 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24248 onRender : function(ct, position)
24251 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24252 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24255 this.el.dom.style.border = '0 none';
24256 this.el.dom.setAttribute('tabIndex', -1);
24257 this.el.addClass('x-hidden hide');
24261 if(Roo.isIE){ // fix IE 1px bogus margin
24262 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24266 this.frameId = Roo.id();
24270 var iframe = this.owner.wrap.createChild({
24272 cls: 'form-control', // bootstrap..
24274 name: this.frameId,
24275 frameBorder : 'no',
24276 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24281 this.iframe = iframe.dom;
24283 this.assignDocWin();
24285 this.doc.designMode = 'on';
24288 this.doc.write(this.getDocMarkup());
24292 var task = { // must defer to wait for browser to be ready
24294 //console.log("run task?" + this.doc.readyState);
24295 this.assignDocWin();
24296 if(this.doc.body || this.doc.readyState == 'complete'){
24298 this.doc.designMode="on";
24302 Roo.TaskMgr.stop(task);
24303 this.initEditor.defer(10, this);
24310 Roo.TaskMgr.start(task);
24315 onResize : function(w, h)
24317 Roo.log('resize: ' +w + ',' + h );
24318 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24322 if(typeof w == 'number'){
24324 this.iframe.style.width = w + 'px';
24326 if(typeof h == 'number'){
24328 this.iframe.style.height = h + 'px';
24330 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24337 * Toggles the editor between standard and source edit mode.
24338 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24340 toggleSourceEdit : function(sourceEditMode){
24342 this.sourceEditMode = sourceEditMode === true;
24344 if(this.sourceEditMode){
24346 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24349 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24350 //this.iframe.className = '';
24353 //this.setSize(this.owner.wrap.getSize());
24354 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24361 * Protected method that will not generally be called directly. If you need/want
24362 * custom HTML cleanup, this is the method you should override.
24363 * @param {String} html The HTML to be cleaned
24364 * return {String} The cleaned HTML
24366 cleanHtml : function(html){
24367 html = String(html);
24368 if(html.length > 5){
24369 if(Roo.isSafari){ // strip safari nonsense
24370 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24373 if(html == ' '){
24380 * HTML Editor -> Textarea
24381 * Protected method that will not generally be called directly. Syncs the contents
24382 * of the editor iframe with the textarea.
24384 syncValue : function(){
24385 if(this.initialized){
24386 var bd = (this.doc.body || this.doc.documentElement);
24387 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24388 var html = bd.innerHTML;
24390 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24391 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24393 html = '<div style="'+m[0]+'">' + html + '</div>';
24396 html = this.cleanHtml(html);
24397 // fix up the special chars.. normaly like back quotes in word...
24398 // however we do not want to do this with chinese..
24399 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24401 var cc = match.charCodeAt();
24403 // Get the character value, handling surrogate pairs
24404 if (match.length == 2) {
24405 // It's a surrogate pair, calculate the Unicode code point
24406 var high = match.charCodeAt(0) - 0xD800;
24407 var low = match.charCodeAt(1) - 0xDC00;
24408 cc = (high * 0x400) + low + 0x10000;
24410 (cc >= 0x4E00 && cc < 0xA000 ) ||
24411 (cc >= 0x3400 && cc < 0x4E00 ) ||
24412 (cc >= 0xf900 && cc < 0xfb00 )
24417 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24418 return "&#" + cc + ";";
24425 if(this.owner.fireEvent('beforesync', this, html) !== false){
24426 this.el.dom.value = html;
24427 this.owner.fireEvent('sync', this, html);
24433 * Protected method that will not generally be called directly. Pushes the value of the textarea
24434 * into the iframe editor.
24436 pushValue : function(){
24437 if(this.initialized){
24438 var v = this.el.dom.value.trim();
24440 // if(v.length < 1){
24444 if(this.owner.fireEvent('beforepush', this, v) !== false){
24445 var d = (this.doc.body || this.doc.documentElement);
24447 this.cleanUpPaste();
24448 this.el.dom.value = d.innerHTML;
24449 this.owner.fireEvent('push', this, v);
24455 deferFocus : function(){
24456 this.focus.defer(10, this);
24460 focus : function(){
24461 if(this.win && !this.sourceEditMode){
24468 assignDocWin: function()
24470 var iframe = this.iframe;
24473 this.doc = iframe.contentWindow.document;
24474 this.win = iframe.contentWindow;
24476 // if (!Roo.get(this.frameId)) {
24479 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24480 // this.win = Roo.get(this.frameId).dom.contentWindow;
24482 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24486 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24487 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24492 initEditor : function(){
24493 //console.log("INIT EDITOR");
24494 this.assignDocWin();
24498 this.doc.designMode="on";
24500 this.doc.write(this.getDocMarkup());
24503 var dbody = (this.doc.body || this.doc.documentElement);
24504 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24505 // this copies styles from the containing element into thsi one..
24506 // not sure why we need all of this..
24507 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24509 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24510 //ss['background-attachment'] = 'fixed'; // w3c
24511 dbody.bgProperties = 'fixed'; // ie
24512 //Roo.DomHelper.applyStyles(dbody, ss);
24513 Roo.EventManager.on(this.doc, {
24514 //'mousedown': this.onEditorEvent,
24515 'mouseup': this.onEditorEvent,
24516 'dblclick': this.onEditorEvent,
24517 'click': this.onEditorEvent,
24518 'keyup': this.onEditorEvent,
24523 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24525 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24526 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24528 this.initialized = true;
24530 this.owner.fireEvent('initialize', this);
24535 onDestroy : function(){
24541 //for (var i =0; i < this.toolbars.length;i++) {
24542 // // fixme - ask toolbars for heights?
24543 // this.toolbars[i].onDestroy();
24546 //this.wrap.dom.innerHTML = '';
24547 //this.wrap.remove();
24552 onFirstFocus : function(){
24554 this.assignDocWin();
24557 this.activated = true;
24560 if(Roo.isGecko){ // prevent silly gecko errors
24562 var s = this.win.getSelection();
24563 if(!s.focusNode || s.focusNode.nodeType != 3){
24564 var r = s.getRangeAt(0);
24565 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24570 this.execCmd('useCSS', true);
24571 this.execCmd('styleWithCSS', false);
24574 this.owner.fireEvent('activate', this);
24578 adjustFont: function(btn){
24579 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24580 //if(Roo.isSafari){ // safari
24583 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24584 if(Roo.isSafari){ // safari
24585 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24586 v = (v < 10) ? 10 : v;
24587 v = (v > 48) ? 48 : v;
24588 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24593 v = Math.max(1, v+adjust);
24595 this.execCmd('FontSize', v );
24598 onEditorEvent : function(e)
24600 this.owner.fireEvent('editorevent', this, e);
24601 // this.updateToolbar();
24602 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24605 insertTag : function(tg)
24607 // could be a bit smarter... -> wrap the current selected tRoo..
24608 if (tg.toLowerCase() == 'span' ||
24609 tg.toLowerCase() == 'code' ||
24610 tg.toLowerCase() == 'sup' ||
24611 tg.toLowerCase() == 'sub'
24614 range = this.createRange(this.getSelection());
24615 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24616 wrappingNode.appendChild(range.extractContents());
24617 range.insertNode(wrappingNode);
24624 this.execCmd("formatblock", tg);
24628 insertText : function(txt)
24632 var range = this.createRange();
24633 range.deleteContents();
24634 //alert(Sender.getAttribute('label'));
24636 range.insertNode(this.doc.createTextNode(txt));
24642 * Executes a Midas editor command on the editor document and performs necessary focus and
24643 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24644 * @param {String} cmd The Midas command
24645 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24647 relayCmd : function(cmd, value){
24649 this.execCmd(cmd, value);
24650 this.owner.fireEvent('editorevent', this);
24651 //this.updateToolbar();
24652 this.owner.deferFocus();
24656 * Executes a Midas editor command directly on the editor document.
24657 * For visual commands, you should use {@link #relayCmd} instead.
24658 * <b>This should only be called after the editor is initialized.</b>
24659 * @param {String} cmd The Midas command
24660 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24662 execCmd : function(cmd, value){
24663 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24670 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24672 * @param {String} text | dom node..
24674 insertAtCursor : function(text)
24677 if(!this.activated){
24683 var r = this.doc.selection.createRange();
24694 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24698 // from jquery ui (MIT licenced)
24700 var win = this.win;
24702 if (win.getSelection && win.getSelection().getRangeAt) {
24703 range = win.getSelection().getRangeAt(0);
24704 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24705 range.insertNode(node);
24706 } else if (win.document.selection && win.document.selection.createRange) {
24707 // no firefox support
24708 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24709 win.document.selection.createRange().pasteHTML(txt);
24711 // no firefox support
24712 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24713 this.execCmd('InsertHTML', txt);
24722 mozKeyPress : function(e){
24724 var c = e.getCharCode(), cmd;
24727 c = String.fromCharCode(c).toLowerCase();
24741 this.cleanUpPaste.defer(100, this);
24749 e.preventDefault();
24757 fixKeys : function(){ // load time branching for fastest keydown performance
24759 return function(e){
24760 var k = e.getKey(), r;
24763 r = this.doc.selection.createRange();
24766 r.pasteHTML('    ');
24773 r = this.doc.selection.createRange();
24775 var target = r.parentElement();
24776 if(!target || target.tagName.toLowerCase() != 'li'){
24778 r.pasteHTML('<br />');
24784 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24785 this.cleanUpPaste.defer(100, this);
24791 }else if(Roo.isOpera){
24792 return function(e){
24793 var k = e.getKey();
24797 this.execCmd('InsertHTML','    ');
24800 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24801 this.cleanUpPaste.defer(100, this);
24806 }else if(Roo.isSafari){
24807 return function(e){
24808 var k = e.getKey();
24812 this.execCmd('InsertText','\t');
24816 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24817 this.cleanUpPaste.defer(100, this);
24825 getAllAncestors: function()
24827 var p = this.getSelectedNode();
24830 a.push(p); // push blank onto stack..
24831 p = this.getParentElement();
24835 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24839 a.push(this.doc.body);
24843 lastSelNode : false,
24846 getSelection : function()
24848 this.assignDocWin();
24849 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24852 getSelectedNode: function()
24854 // this may only work on Gecko!!!
24856 // should we cache this!!!!
24861 var range = this.createRange(this.getSelection()).cloneRange();
24864 var parent = range.parentElement();
24866 var testRange = range.duplicate();
24867 testRange.moveToElementText(parent);
24868 if (testRange.inRange(range)) {
24871 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24874 parent = parent.parentElement;
24879 // is ancestor a text element.
24880 var ac = range.commonAncestorContainer;
24881 if (ac.nodeType == 3) {
24882 ac = ac.parentNode;
24885 var ar = ac.childNodes;
24888 var other_nodes = [];
24889 var has_other_nodes = false;
24890 for (var i=0;i<ar.length;i++) {
24891 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24894 // fullly contained node.
24896 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24901 // probably selected..
24902 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24903 other_nodes.push(ar[i]);
24907 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24912 has_other_nodes = true;
24914 if (!nodes.length && other_nodes.length) {
24915 nodes= other_nodes;
24917 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24923 createRange: function(sel)
24925 // this has strange effects when using with
24926 // top toolbar - not sure if it's a great idea.
24927 //this.editor.contentWindow.focus();
24928 if (typeof sel != "undefined") {
24930 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24932 return this.doc.createRange();
24935 return this.doc.createRange();
24938 getParentElement: function()
24941 this.assignDocWin();
24942 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24944 var range = this.createRange(sel);
24947 var p = range.commonAncestorContainer;
24948 while (p.nodeType == 3) { // text node
24959 * Range intersection.. the hard stuff...
24963 * [ -- selected range --- ]
24967 * if end is before start or hits it. fail.
24968 * if start is after end or hits it fail.
24970 * if either hits (but other is outside. - then it's not
24976 // @see http://www.thismuchiknow.co.uk/?p=64.
24977 rangeIntersectsNode : function(range, node)
24979 var nodeRange = node.ownerDocument.createRange();
24981 nodeRange.selectNode(node);
24983 nodeRange.selectNodeContents(node);
24986 var rangeStartRange = range.cloneRange();
24987 rangeStartRange.collapse(true);
24989 var rangeEndRange = range.cloneRange();
24990 rangeEndRange.collapse(false);
24992 var nodeStartRange = nodeRange.cloneRange();
24993 nodeStartRange.collapse(true);
24995 var nodeEndRange = nodeRange.cloneRange();
24996 nodeEndRange.collapse(false);
24998 return rangeStartRange.compareBoundaryPoints(
24999 Range.START_TO_START, nodeEndRange) == -1 &&
25000 rangeEndRange.compareBoundaryPoints(
25001 Range.START_TO_START, nodeStartRange) == 1;
25005 rangeCompareNode : function(range, node)
25007 var nodeRange = node.ownerDocument.createRange();
25009 nodeRange.selectNode(node);
25011 nodeRange.selectNodeContents(node);
25015 range.collapse(true);
25017 nodeRange.collapse(true);
25019 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25020 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25022 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25024 var nodeIsBefore = ss == 1;
25025 var nodeIsAfter = ee == -1;
25027 if (nodeIsBefore && nodeIsAfter) {
25030 if (!nodeIsBefore && nodeIsAfter) {
25031 return 1; //right trailed.
25034 if (nodeIsBefore && !nodeIsAfter) {
25035 return 2; // left trailed.
25041 // private? - in a new class?
25042 cleanUpPaste : function()
25044 // cleans up the whole document..
25045 Roo.log('cleanuppaste');
25047 this.cleanUpChildren(this.doc.body);
25048 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25049 if (clean != this.doc.body.innerHTML) {
25050 this.doc.body.innerHTML = clean;
25055 cleanWordChars : function(input) {// change the chars to hex code
25056 var he = Roo.HtmlEditorCore;
25058 var output = input;
25059 Roo.each(he.swapCodes, function(sw) {
25060 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25062 output = output.replace(swapper, sw[1]);
25069 cleanUpChildren : function (n)
25071 if (!n.childNodes.length) {
25074 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25075 this.cleanUpChild(n.childNodes[i]);
25082 cleanUpChild : function (node)
25085 //console.log(node);
25086 if (node.nodeName == "#text") {
25087 // clean up silly Windows -- stuff?
25090 if (node.nodeName == "#comment") {
25091 node.parentNode.removeChild(node);
25092 // clean up silly Windows -- stuff?
25095 var lcname = node.tagName.toLowerCase();
25096 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25097 // whitelist of tags..
25099 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25101 node.parentNode.removeChild(node);
25106 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25108 // spans with no attributes - just remove them..
25109 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25110 remove_keep_children = true;
25113 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25114 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25116 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25117 // remove_keep_children = true;
25120 if (remove_keep_children) {
25121 this.cleanUpChildren(node);
25122 // inserts everything just before this node...
25123 while (node.childNodes.length) {
25124 var cn = node.childNodes[0];
25125 node.removeChild(cn);
25126 node.parentNode.insertBefore(cn, node);
25128 node.parentNode.removeChild(node);
25132 if (!node.attributes || !node.attributes.length) {
25137 this.cleanUpChildren(node);
25141 function cleanAttr(n,v)
25144 if (v.match(/^\./) || v.match(/^\//)) {
25147 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25150 if (v.match(/^#/)) {
25153 if (v.match(/^\{/)) { // allow template editing.
25156 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25157 node.removeAttribute(n);
25161 var cwhite = this.cwhite;
25162 var cblack = this.cblack;
25164 function cleanStyle(n,v)
25166 if (v.match(/expression/)) { //XSS?? should we even bother..
25167 node.removeAttribute(n);
25171 var parts = v.split(/;/);
25174 Roo.each(parts, function(p) {
25175 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25179 var l = p.split(':').shift().replace(/\s+/g,'');
25180 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25182 if ( cwhite.length && cblack.indexOf(l) > -1) {
25183 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25184 //node.removeAttribute(n);
25188 // only allow 'c whitelisted system attributes'
25189 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25190 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25191 //node.removeAttribute(n);
25201 if (clean.length) {
25202 node.setAttribute(n, clean.join(';'));
25204 node.removeAttribute(n);
25210 for (var i = node.attributes.length-1; i > -1 ; i--) {
25211 var a = node.attributes[i];
25214 if (a.name.toLowerCase().substr(0,2)=='on') {
25215 node.removeAttribute(a.name);
25218 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25219 node.removeAttribute(a.name);
25222 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25223 cleanAttr(a.name,a.value); // fixme..
25226 if (a.name == 'style') {
25227 cleanStyle(a.name,a.value);
25230 /// clean up MS crap..
25231 // tecnically this should be a list of valid class'es..
25234 if (a.name == 'class') {
25235 if (a.value.match(/^Mso/)) {
25236 node.removeAttribute('class');
25239 if (a.value.match(/^body$/)) {
25240 node.removeAttribute('class');
25251 this.cleanUpChildren(node);
25257 * Clean up MS wordisms...
25259 cleanWord : function(node)
25262 this.cleanWord(this.doc.body);
25267 node.nodeName == 'SPAN' &&
25268 !node.hasAttributes() &&
25269 node.childNodes.length == 1 &&
25270 node.firstChild.nodeName == "#text"
25272 var textNode = node.firstChild;
25273 node.removeChild(textNode);
25274 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25275 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25277 node.parentNode.insertBefore(textNode, node);
25278 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25279 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25281 node.parentNode.removeChild(node);
25284 if (node.nodeName == "#text") {
25285 // clean up silly Windows -- stuff?
25288 if (node.nodeName == "#comment") {
25289 node.parentNode.removeChild(node);
25290 // clean up silly Windows -- stuff?
25294 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25295 node.parentNode.removeChild(node);
25298 //Roo.log(node.tagName);
25299 // remove - but keep children..
25300 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25301 //Roo.log('-- removed');
25302 while (node.childNodes.length) {
25303 var cn = node.childNodes[0];
25304 node.removeChild(cn);
25305 node.parentNode.insertBefore(cn, node);
25306 // move node to parent - and clean it..
25307 this.cleanWord(cn);
25309 node.parentNode.removeChild(node);
25310 /// no need to iterate chidlren = it's got none..
25311 //this.iterateChildren(node, this.cleanWord);
25315 if (node.className.length) {
25317 var cn = node.className.split(/\W+/);
25319 Roo.each(cn, function(cls) {
25320 if (cls.match(/Mso[a-zA-Z]+/)) {
25325 node.className = cna.length ? cna.join(' ') : '';
25327 node.removeAttribute("class");
25331 if (node.hasAttribute("lang")) {
25332 node.removeAttribute("lang");
25335 if (node.hasAttribute("style")) {
25337 var styles = node.getAttribute("style").split(";");
25339 Roo.each(styles, function(s) {
25340 if (!s.match(/:/)) {
25343 var kv = s.split(":");
25344 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25347 // what ever is left... we allow.
25350 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25351 if (!nstyle.length) {
25352 node.removeAttribute('style');
25355 this.iterateChildren(node, this.cleanWord);
25361 * iterateChildren of a Node, calling fn each time, using this as the scole..
25362 * @param {DomNode} node node to iterate children of.
25363 * @param {Function} fn method of this class to call on each item.
25365 iterateChildren : function(node, fn)
25367 if (!node.childNodes.length) {
25370 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25371 fn.call(this, node.childNodes[i])
25377 * cleanTableWidths.
25379 * Quite often pasting from word etc.. results in tables with column and widths.
25380 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25383 cleanTableWidths : function(node)
25388 this.cleanTableWidths(this.doc.body);
25393 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25396 Roo.log(node.tagName);
25397 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25398 this.iterateChildren(node, this.cleanTableWidths);
25401 if (node.hasAttribute('width')) {
25402 node.removeAttribute('width');
25406 if (node.hasAttribute("style")) {
25409 var styles = node.getAttribute("style").split(";");
25411 Roo.each(styles, function(s) {
25412 if (!s.match(/:/)) {
25415 var kv = s.split(":");
25416 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25419 // what ever is left... we allow.
25422 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25423 if (!nstyle.length) {
25424 node.removeAttribute('style');
25428 this.iterateChildren(node, this.cleanTableWidths);
25436 domToHTML : function(currentElement, depth, nopadtext) {
25438 depth = depth || 0;
25439 nopadtext = nopadtext || false;
25441 if (!currentElement) {
25442 return this.domToHTML(this.doc.body);
25445 //Roo.log(currentElement);
25447 var allText = false;
25448 var nodeName = currentElement.nodeName;
25449 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25451 if (nodeName == '#text') {
25453 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25458 if (nodeName != 'BODY') {
25461 // Prints the node tagName, such as <A>, <IMG>, etc
25464 for(i = 0; i < currentElement.attributes.length;i++) {
25466 var aname = currentElement.attributes.item(i).name;
25467 if (!currentElement.attributes.item(i).value.length) {
25470 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25473 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25482 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25485 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25490 // Traverse the tree
25492 var currentElementChild = currentElement.childNodes.item(i);
25493 var allText = true;
25494 var innerHTML = '';
25496 while (currentElementChild) {
25497 // Formatting code (indent the tree so it looks nice on the screen)
25498 var nopad = nopadtext;
25499 if (lastnode == 'SPAN') {
25503 if (currentElementChild.nodeName == '#text') {
25504 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25505 toadd = nopadtext ? toadd : toadd.trim();
25506 if (!nopad && toadd.length > 80) {
25507 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25509 innerHTML += toadd;
25512 currentElementChild = currentElement.childNodes.item(i);
25518 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25520 // Recursively traverse the tree structure of the child node
25521 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25522 lastnode = currentElementChild.nodeName;
25524 currentElementChild=currentElement.childNodes.item(i);
25530 // The remaining code is mostly for formatting the tree
25531 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25536 ret+= "</"+tagName+">";
25542 applyBlacklists : function()
25544 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25545 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25549 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25550 if (b.indexOf(tag) > -1) {
25553 this.white.push(tag);
25557 Roo.each(w, function(tag) {
25558 if (b.indexOf(tag) > -1) {
25561 if (this.white.indexOf(tag) > -1) {
25564 this.white.push(tag);
25569 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25570 if (w.indexOf(tag) > -1) {
25573 this.black.push(tag);
25577 Roo.each(b, function(tag) {
25578 if (w.indexOf(tag) > -1) {
25581 if (this.black.indexOf(tag) > -1) {
25584 this.black.push(tag);
25589 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25590 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25594 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25595 if (b.indexOf(tag) > -1) {
25598 this.cwhite.push(tag);
25602 Roo.each(w, function(tag) {
25603 if (b.indexOf(tag) > -1) {
25606 if (this.cwhite.indexOf(tag) > -1) {
25609 this.cwhite.push(tag);
25614 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25615 if (w.indexOf(tag) > -1) {
25618 this.cblack.push(tag);
25622 Roo.each(b, function(tag) {
25623 if (w.indexOf(tag) > -1) {
25626 if (this.cblack.indexOf(tag) > -1) {
25629 this.cblack.push(tag);
25634 setStylesheets : function(stylesheets)
25636 if(typeof(stylesheets) == 'string'){
25637 Roo.get(this.iframe.contentDocument.head).createChild({
25639 rel : 'stylesheet',
25648 Roo.each(stylesheets, function(s) {
25653 Roo.get(_this.iframe.contentDocument.head).createChild({
25655 rel : 'stylesheet',
25664 removeStylesheets : function()
25668 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25673 setStyle : function(style)
25675 Roo.get(this.iframe.contentDocument.head).createChild({
25684 // hide stuff that is not compatible
25698 * @event specialkey
25702 * @cfg {String} fieldClass @hide
25705 * @cfg {String} focusClass @hide
25708 * @cfg {String} autoCreate @hide
25711 * @cfg {String} inputType @hide
25714 * @cfg {String} invalidClass @hide
25717 * @cfg {String} invalidText @hide
25720 * @cfg {String} msgFx @hide
25723 * @cfg {String} validateOnBlur @hide
25727 Roo.HtmlEditorCore.white = [
25728 'area', 'br', 'img', 'input', 'hr', 'wbr',
25730 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25731 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25732 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25733 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25734 'table', 'ul', 'xmp',
25736 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25739 'dir', 'menu', 'ol', 'ul', 'dl',
25745 Roo.HtmlEditorCore.black = [
25746 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25748 'base', 'basefont', 'bgsound', 'blink', 'body',
25749 'frame', 'frameset', 'head', 'html', 'ilayer',
25750 'iframe', 'layer', 'link', 'meta', 'object',
25751 'script', 'style' ,'title', 'xml' // clean later..
25753 Roo.HtmlEditorCore.clean = [
25754 'script', 'style', 'title', 'xml'
25756 Roo.HtmlEditorCore.remove = [
25761 Roo.HtmlEditorCore.ablack = [
25765 Roo.HtmlEditorCore.aclean = [
25766 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25770 Roo.HtmlEditorCore.pwhite= [
25771 'http', 'https', 'mailto'
25774 // white listed style attributes.
25775 Roo.HtmlEditorCore.cwhite= [
25776 // 'text-align', /// default is to allow most things..
25782 // black listed style attributes.
25783 Roo.HtmlEditorCore.cblack= [
25784 // 'font-size' -- this can be set by the project
25788 Roo.HtmlEditorCore.swapCodes =[
25807 * @class Roo.bootstrap.HtmlEditor
25808 * @extends Roo.bootstrap.TextArea
25809 * Bootstrap HtmlEditor class
25812 * Create a new HtmlEditor
25813 * @param {Object} config The config object
25816 Roo.bootstrap.HtmlEditor = function(config){
25817 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25818 if (!this.toolbars) {
25819 this.toolbars = [];
25822 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25825 * @event initialize
25826 * Fires when the editor is fully initialized (including the iframe)
25827 * @param {HtmlEditor} this
25832 * Fires when the editor is first receives the focus. Any insertion must wait
25833 * until after this event.
25834 * @param {HtmlEditor} this
25838 * @event beforesync
25839 * Fires before the textarea is updated with content from the editor iframe. Return false
25840 * to cancel the sync.
25841 * @param {HtmlEditor} this
25842 * @param {String} html
25846 * @event beforepush
25847 * Fires before the iframe editor is updated with content from the textarea. Return false
25848 * to cancel the push.
25849 * @param {HtmlEditor} this
25850 * @param {String} html
25855 * Fires when the textarea is updated with content from the editor iframe.
25856 * @param {HtmlEditor} this
25857 * @param {String} html
25862 * Fires when the iframe editor is updated with content from the textarea.
25863 * @param {HtmlEditor} this
25864 * @param {String} html
25868 * @event editmodechange
25869 * Fires when the editor switches edit modes
25870 * @param {HtmlEditor} this
25871 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25873 editmodechange: true,
25875 * @event editorevent
25876 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25877 * @param {HtmlEditor} this
25881 * @event firstfocus
25882 * Fires when on first focus - needed by toolbars..
25883 * @param {HtmlEditor} this
25888 * Auto save the htmlEditor value as a file into Events
25889 * @param {HtmlEditor} this
25893 * @event savedpreview
25894 * preview the saved version of htmlEditor
25895 * @param {HtmlEditor} this
25902 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25906 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25911 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25916 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25921 * @cfg {Number} height (in pixels)
25925 * @cfg {Number} width (in pixels)
25930 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25933 stylesheets: false,
25938 // private properties
25939 validationEvent : false,
25941 initialized : false,
25944 onFocus : Roo.emptyFn,
25946 hideMode:'offsets',
25948 tbContainer : false,
25952 toolbarContainer :function() {
25953 return this.wrap.select('.x-html-editor-tb',true).first();
25957 * Protected method that will not generally be called directly. It
25958 * is called when the editor creates its toolbar. Override this method if you need to
25959 * add custom toolbar buttons.
25960 * @param {HtmlEditor} editor
25962 createToolbar : function(){
25963 Roo.log('renewing');
25964 Roo.log("create toolbars");
25966 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25967 this.toolbars[0].render(this.toolbarContainer());
25971 // if (!editor.toolbars || !editor.toolbars.length) {
25972 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25975 // for (var i =0 ; i < editor.toolbars.length;i++) {
25976 // editor.toolbars[i] = Roo.factory(
25977 // typeof(editor.toolbars[i]) == 'string' ?
25978 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25979 // Roo.bootstrap.HtmlEditor);
25980 // editor.toolbars[i].init(editor);
25986 onRender : function(ct, position)
25988 // Roo.log("Call onRender: " + this.xtype);
25990 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25992 this.wrap = this.inputEl().wrap({
25993 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25996 this.editorcore.onRender(ct, position);
25998 if (this.resizable) {
25999 this.resizeEl = new Roo.Resizable(this.wrap, {
26003 minHeight : this.height,
26004 height: this.height,
26005 handles : this.resizable,
26008 resize : function(r, w, h) {
26009 _t.onResize(w,h); // -something
26015 this.createToolbar(this);
26018 if(!this.width && this.resizable){
26019 this.setSize(this.wrap.getSize());
26021 if (this.resizeEl) {
26022 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26023 // should trigger onReize..
26029 onResize : function(w, h)
26031 Roo.log('resize: ' +w + ',' + h );
26032 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26036 if(this.inputEl() ){
26037 if(typeof w == 'number'){
26038 var aw = w - this.wrap.getFrameWidth('lr');
26039 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26042 if(typeof h == 'number'){
26043 var tbh = -11; // fixme it needs to tool bar size!
26044 for (var i =0; i < this.toolbars.length;i++) {
26045 // fixme - ask toolbars for heights?
26046 tbh += this.toolbars[i].el.getHeight();
26047 //if (this.toolbars[i].footer) {
26048 // tbh += this.toolbars[i].footer.el.getHeight();
26056 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26057 ah -= 5; // knock a few pixes off for look..
26058 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26062 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26063 this.editorcore.onResize(ew,eh);
26068 * Toggles the editor between standard and source edit mode.
26069 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26071 toggleSourceEdit : function(sourceEditMode)
26073 this.editorcore.toggleSourceEdit(sourceEditMode);
26075 if(this.editorcore.sourceEditMode){
26076 Roo.log('editor - showing textarea');
26079 // Roo.log(this.syncValue());
26081 this.inputEl().removeClass(['hide', 'x-hidden']);
26082 this.inputEl().dom.removeAttribute('tabIndex');
26083 this.inputEl().focus();
26085 Roo.log('editor - hiding textarea');
26087 // Roo.log(this.pushValue());
26090 this.inputEl().addClass(['hide', 'x-hidden']);
26091 this.inputEl().dom.setAttribute('tabIndex', -1);
26092 //this.deferFocus();
26095 if(this.resizable){
26096 this.setSize(this.wrap.getSize());
26099 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26102 // private (for BoxComponent)
26103 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26105 // private (for BoxComponent)
26106 getResizeEl : function(){
26110 // private (for BoxComponent)
26111 getPositionEl : function(){
26116 initEvents : function(){
26117 this.originalValue = this.getValue();
26121 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26124 // markInvalid : Roo.emptyFn,
26126 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26129 // clearInvalid : Roo.emptyFn,
26131 setValue : function(v){
26132 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26133 this.editorcore.pushValue();
26138 deferFocus : function(){
26139 this.focus.defer(10, this);
26143 focus : function(){
26144 this.editorcore.focus();
26150 onDestroy : function(){
26156 for (var i =0; i < this.toolbars.length;i++) {
26157 // fixme - ask toolbars for heights?
26158 this.toolbars[i].onDestroy();
26161 this.wrap.dom.innerHTML = '';
26162 this.wrap.remove();
26167 onFirstFocus : function(){
26168 //Roo.log("onFirstFocus");
26169 this.editorcore.onFirstFocus();
26170 for (var i =0; i < this.toolbars.length;i++) {
26171 this.toolbars[i].onFirstFocus();
26177 syncValue : function()
26179 this.editorcore.syncValue();
26182 pushValue : function()
26184 this.editorcore.pushValue();
26188 // hide stuff that is not compatible
26202 * @event specialkey
26206 * @cfg {String} fieldClass @hide
26209 * @cfg {String} focusClass @hide
26212 * @cfg {String} autoCreate @hide
26215 * @cfg {String} inputType @hide
26219 * @cfg {String} invalidText @hide
26222 * @cfg {String} msgFx @hide
26225 * @cfg {String} validateOnBlur @hide
26234 Roo.namespace('Roo.bootstrap.htmleditor');
26236 * @class Roo.bootstrap.HtmlEditorToolbar1
26242 new Roo.bootstrap.HtmlEditor({
26245 new Roo.bootstrap.HtmlEditorToolbar1({
26246 disable : { fonts: 1 , format: 1, ..., ... , ...],
26252 * @cfg {Object} disable List of elements to disable..
26253 * @cfg {Array} btns List of additional buttons.
26257 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26260 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26263 Roo.apply(this, config);
26265 // default disabled, based on 'good practice'..
26266 this.disable = this.disable || {};
26267 Roo.applyIf(this.disable, {
26270 specialElements : true
26272 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26274 this.editor = config.editor;
26275 this.editorcore = config.editor.editorcore;
26277 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26279 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26280 // dont call parent... till later.
26282 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26287 editorcore : false,
26292 "h1","h2","h3","h4","h5","h6",
26294 "abbr", "acronym", "address", "cite", "samp", "var",
26298 onRender : function(ct, position)
26300 // Roo.log("Call onRender: " + this.xtype);
26302 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26304 this.el.dom.style.marginBottom = '0';
26306 var editorcore = this.editorcore;
26307 var editor= this.editor;
26310 var btn = function(id,cmd , toggle, handler, html){
26312 var event = toggle ? 'toggle' : 'click';
26317 xns: Roo.bootstrap,
26321 enableToggle:toggle !== false,
26323 pressed : toggle ? false : null,
26326 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26327 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26333 // var cb_box = function...
26338 xns: Roo.bootstrap,
26343 xns: Roo.bootstrap,
26347 Roo.each(this.formats, function(f) {
26348 style.menu.items.push({
26350 xns: Roo.bootstrap,
26351 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26356 editorcore.insertTag(this.tagname);
26363 children.push(style);
26365 btn('bold',false,true);
26366 btn('italic',false,true);
26367 btn('align-left', 'justifyleft',true);
26368 btn('align-center', 'justifycenter',true);
26369 btn('align-right' , 'justifyright',true);
26370 btn('link', false, false, function(btn) {
26371 //Roo.log("create link?");
26372 var url = prompt(this.createLinkText, this.defaultLinkValue);
26373 if(url && url != 'http:/'+'/'){
26374 this.editorcore.relayCmd('createlink', url);
26377 btn('list','insertunorderedlist',true);
26378 btn('pencil', false,true, function(btn){
26380 this.toggleSourceEdit(btn.pressed);
26383 if (this.editor.btns.length > 0) {
26384 for (var i = 0; i<this.editor.btns.length; i++) {
26385 children.push(this.editor.btns[i]);
26393 xns: Roo.bootstrap,
26398 xns: Roo.bootstrap,
26403 cog.menu.items.push({
26405 xns: Roo.bootstrap,
26406 html : Clean styles,
26411 editorcore.insertTag(this.tagname);
26420 this.xtype = 'NavSimplebar';
26422 for(var i=0;i< children.length;i++) {
26424 this.buttons.add(this.addxtypeChild(children[i]));
26428 editor.on('editorevent', this.updateToolbar, this);
26430 onBtnClick : function(id)
26432 this.editorcore.relayCmd(id);
26433 this.editorcore.focus();
26437 * Protected method that will not generally be called directly. It triggers
26438 * a toolbar update by reading the markup state of the current selection in the editor.
26440 updateToolbar: function(){
26442 if(!this.editorcore.activated){
26443 this.editor.onFirstFocus(); // is this neeed?
26447 var btns = this.buttons;
26448 var doc = this.editorcore.doc;
26449 btns.get('bold').setActive(doc.queryCommandState('bold'));
26450 btns.get('italic').setActive(doc.queryCommandState('italic'));
26451 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26453 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26454 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26455 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26457 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26458 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26461 var ans = this.editorcore.getAllAncestors();
26462 if (this.formatCombo) {
26465 var store = this.formatCombo.store;
26466 this.formatCombo.setValue("");
26467 for (var i =0; i < ans.length;i++) {
26468 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26470 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26478 // hides menus... - so this cant be on a menu...
26479 Roo.bootstrap.MenuMgr.hideAll();
26481 Roo.bootstrap.MenuMgr.hideAll();
26482 //this.editorsyncValue();
26484 onFirstFocus: function() {
26485 this.buttons.each(function(item){
26489 toggleSourceEdit : function(sourceEditMode){
26492 if(sourceEditMode){
26493 Roo.log("disabling buttons");
26494 this.buttons.each( function(item){
26495 if(item.cmd != 'pencil'){
26501 Roo.log("enabling buttons");
26502 if(this.editorcore.initialized){
26503 this.buttons.each( function(item){
26509 Roo.log("calling toggole on editor");
26510 // tell the editor that it's been pressed..
26511 this.editor.toggleSourceEdit(sourceEditMode);
26525 * @class Roo.bootstrap.Markdown
26526 * @extends Roo.bootstrap.TextArea
26527 * Bootstrap Showdown editable area
26528 * @cfg {string} content
26531 * Create a new Showdown
26534 Roo.bootstrap.Markdown = function(config){
26535 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26539 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26543 initEvents : function()
26546 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26547 this.markdownEl = this.el.createChild({
26548 cls : 'roo-markdown-area'
26550 this.inputEl().addClass('d-none');
26551 if (this.getValue() == '') {
26552 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26555 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26557 this.markdownEl.on('click', this.toggleTextEdit, this);
26558 this.on('blur', this.toggleTextEdit, this);
26559 this.on('specialkey', this.resizeTextArea, this);
26562 toggleTextEdit : function()
26564 var sh = this.markdownEl.getHeight();
26565 this.inputEl().addClass('d-none');
26566 this.markdownEl.addClass('d-none');
26567 if (!this.editing) {
26569 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26570 this.inputEl().removeClass('d-none');
26571 this.inputEl().focus();
26572 this.editing = true;
26575 // show showdown...
26576 this.updateMarkdown();
26577 this.markdownEl.removeClass('d-none');
26578 this.editing = false;
26581 updateMarkdown : function()
26583 if (this.getValue() == '') {
26584 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26588 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26591 resizeTextArea: function () {
26594 Roo.log([sh, this.getValue().split("\n").length * 30]);
26595 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26597 setValue : function(val)
26599 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26600 if (!this.editing) {
26601 this.updateMarkdown();
26607 if (!this.editing) {
26608 this.toggleTextEdit();
26616 * @class Roo.bootstrap.Table.AbstractSelectionModel
26617 * @extends Roo.util.Observable
26618 * Abstract base class for grid SelectionModels. It provides the interface that should be
26619 * implemented by descendant classes. This class should not be directly instantiated.
26622 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26623 this.locked = false;
26624 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26628 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26629 /** @ignore Called by the grid automatically. Do not call directly. */
26630 init : function(grid){
26636 * Locks the selections.
26639 this.locked = true;
26643 * Unlocks the selections.
26645 unlock : function(){
26646 this.locked = false;
26650 * Returns true if the selections are locked.
26651 * @return {Boolean}
26653 isLocked : function(){
26654 return this.locked;
26658 initEvents : function ()
26664 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26665 * @class Roo.bootstrap.Table.RowSelectionModel
26666 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26667 * It supports multiple selections and keyboard selection/navigation.
26669 * @param {Object} config
26672 Roo.bootstrap.Table.RowSelectionModel = function(config){
26673 Roo.apply(this, config);
26674 this.selections = new Roo.util.MixedCollection(false, function(o){
26679 this.lastActive = false;
26683 * @event selectionchange
26684 * Fires when the selection changes
26685 * @param {SelectionModel} this
26687 "selectionchange" : true,
26689 * @event afterselectionchange
26690 * Fires after the selection changes (eg. by key press or clicking)
26691 * @param {SelectionModel} this
26693 "afterselectionchange" : true,
26695 * @event beforerowselect
26696 * Fires when a row is selected being selected, return false to cancel.
26697 * @param {SelectionModel} this
26698 * @param {Number} rowIndex The selected index
26699 * @param {Boolean} keepExisting False if other selections will be cleared
26701 "beforerowselect" : true,
26704 * Fires when a row is selected.
26705 * @param {SelectionModel} this
26706 * @param {Number} rowIndex The selected index
26707 * @param {Roo.data.Record} r The record
26709 "rowselect" : true,
26711 * @event rowdeselect
26712 * Fires when a row is deselected.
26713 * @param {SelectionModel} this
26714 * @param {Number} rowIndex The selected index
26716 "rowdeselect" : true
26718 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26719 this.locked = false;
26722 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26724 * @cfg {Boolean} singleSelect
26725 * True to allow selection of only one row at a time (defaults to false)
26727 singleSelect : false,
26730 initEvents : function()
26733 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26734 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26735 //}else{ // allow click to work like normal
26736 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26738 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26739 this.grid.on("rowclick", this.handleMouseDown, this);
26741 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26742 "up" : function(e){
26744 this.selectPrevious(e.shiftKey);
26745 }else if(this.last !== false && this.lastActive !== false){
26746 var last = this.last;
26747 this.selectRange(this.last, this.lastActive-1);
26748 this.grid.getView().focusRow(this.lastActive);
26749 if(last !== false){
26753 this.selectFirstRow();
26755 this.fireEvent("afterselectionchange", this);
26757 "down" : function(e){
26759 this.selectNext(e.shiftKey);
26760 }else if(this.last !== false && this.lastActive !== false){
26761 var last = this.last;
26762 this.selectRange(this.last, this.lastActive+1);
26763 this.grid.getView().focusRow(this.lastActive);
26764 if(last !== false){
26768 this.selectFirstRow();
26770 this.fireEvent("afterselectionchange", this);
26774 this.grid.store.on('load', function(){
26775 this.selections.clear();
26778 var view = this.grid.view;
26779 view.on("refresh", this.onRefresh, this);
26780 view.on("rowupdated", this.onRowUpdated, this);
26781 view.on("rowremoved", this.onRemove, this);
26786 onRefresh : function()
26788 var ds = this.grid.store, i, v = this.grid.view;
26789 var s = this.selections;
26790 s.each(function(r){
26791 if((i = ds.indexOfId(r.id)) != -1){
26800 onRemove : function(v, index, r){
26801 this.selections.remove(r);
26805 onRowUpdated : function(v, index, r){
26806 if(this.isSelected(r)){
26807 v.onRowSelect(index);
26813 * @param {Array} records The records to select
26814 * @param {Boolean} keepExisting (optional) True to keep existing selections
26816 selectRecords : function(records, keepExisting)
26819 this.clearSelections();
26821 var ds = this.grid.store;
26822 for(var i = 0, len = records.length; i < len; i++){
26823 this.selectRow(ds.indexOf(records[i]), true);
26828 * Gets the number of selected rows.
26831 getCount : function(){
26832 return this.selections.length;
26836 * Selects the first row in the grid.
26838 selectFirstRow : function(){
26843 * Select the last row.
26844 * @param {Boolean} keepExisting (optional) True to keep existing selections
26846 selectLastRow : function(keepExisting){
26847 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26848 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26852 * Selects the row immediately following the last selected row.
26853 * @param {Boolean} keepExisting (optional) True to keep existing selections
26855 selectNext : function(keepExisting)
26857 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26858 this.selectRow(this.last+1, keepExisting);
26859 this.grid.getView().focusRow(this.last);
26864 * Selects the row that precedes the last selected row.
26865 * @param {Boolean} keepExisting (optional) True to keep existing selections
26867 selectPrevious : function(keepExisting){
26869 this.selectRow(this.last-1, keepExisting);
26870 this.grid.getView().focusRow(this.last);
26875 * Returns the selected records
26876 * @return {Array} Array of selected records
26878 getSelections : function(){
26879 return [].concat(this.selections.items);
26883 * Returns the first selected record.
26886 getSelected : function(){
26887 return this.selections.itemAt(0);
26892 * Clears all selections.
26894 clearSelections : function(fast)
26900 var ds = this.grid.store;
26901 var s = this.selections;
26902 s.each(function(r){
26903 this.deselectRow(ds.indexOfId(r.id));
26907 this.selections.clear();
26914 * Selects all rows.
26916 selectAll : function(){
26920 this.selections.clear();
26921 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26922 this.selectRow(i, true);
26927 * Returns True if there is a selection.
26928 * @return {Boolean}
26930 hasSelection : function(){
26931 return this.selections.length > 0;
26935 * Returns True if the specified row is selected.
26936 * @param {Number/Record} record The record or index of the record to check
26937 * @return {Boolean}
26939 isSelected : function(index){
26940 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26941 return (r && this.selections.key(r.id) ? true : false);
26945 * Returns True if the specified record id is selected.
26946 * @param {String} id The id of record to check
26947 * @return {Boolean}
26949 isIdSelected : function(id){
26950 return (this.selections.key(id) ? true : false);
26955 handleMouseDBClick : function(e, t){
26959 handleMouseDown : function(e, t)
26961 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26962 if(this.isLocked() || rowIndex < 0 ){
26965 if(e.shiftKey && this.last !== false){
26966 var last = this.last;
26967 this.selectRange(last, rowIndex, e.ctrlKey);
26968 this.last = last; // reset the last
26972 var isSelected = this.isSelected(rowIndex);
26973 //Roo.log("select row:" + rowIndex);
26975 this.deselectRow(rowIndex);
26977 this.selectRow(rowIndex, true);
26981 if(e.button !== 0 && isSelected){
26982 alert('rowIndex 2: ' + rowIndex);
26983 view.focusRow(rowIndex);
26984 }else if(e.ctrlKey && isSelected){
26985 this.deselectRow(rowIndex);
26986 }else if(!isSelected){
26987 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26988 view.focusRow(rowIndex);
26992 this.fireEvent("afterselectionchange", this);
26995 handleDragableRowClick : function(grid, rowIndex, e)
26997 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26998 this.selectRow(rowIndex, false);
26999 grid.view.focusRow(rowIndex);
27000 this.fireEvent("afterselectionchange", this);
27005 * Selects multiple rows.
27006 * @param {Array} rows Array of the indexes of the row to select
27007 * @param {Boolean} keepExisting (optional) True to keep existing selections
27009 selectRows : function(rows, keepExisting){
27011 this.clearSelections();
27013 for(var i = 0, len = rows.length; i < len; i++){
27014 this.selectRow(rows[i], true);
27019 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27020 * @param {Number} startRow The index of the first row in the range
27021 * @param {Number} endRow The index of the last row in the range
27022 * @param {Boolean} keepExisting (optional) True to retain existing selections
27024 selectRange : function(startRow, endRow, keepExisting){
27029 this.clearSelections();
27031 if(startRow <= endRow){
27032 for(var i = startRow; i <= endRow; i++){
27033 this.selectRow(i, true);
27036 for(var i = startRow; i >= endRow; i--){
27037 this.selectRow(i, true);
27043 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27044 * @param {Number} startRow The index of the first row in the range
27045 * @param {Number} endRow The index of the last row in the range
27047 deselectRange : function(startRow, endRow, preventViewNotify){
27051 for(var i = startRow; i <= endRow; i++){
27052 this.deselectRow(i, preventViewNotify);
27058 * @param {Number} row The index of the row to select
27059 * @param {Boolean} keepExisting (optional) True to keep existing selections
27061 selectRow : function(index, keepExisting, preventViewNotify)
27063 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27066 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27067 if(!keepExisting || this.singleSelect){
27068 this.clearSelections();
27071 var r = this.grid.store.getAt(index);
27072 //console.log('selectRow - record id :' + r.id);
27074 this.selections.add(r);
27075 this.last = this.lastActive = index;
27076 if(!preventViewNotify){
27077 var proxy = new Roo.Element(
27078 this.grid.getRowDom(index)
27080 proxy.addClass('bg-info info');
27082 this.fireEvent("rowselect", this, index, r);
27083 this.fireEvent("selectionchange", this);
27089 * @param {Number} row The index of the row to deselect
27091 deselectRow : function(index, preventViewNotify)
27096 if(this.last == index){
27099 if(this.lastActive == index){
27100 this.lastActive = false;
27103 var r = this.grid.store.getAt(index);
27108 this.selections.remove(r);
27109 //.console.log('deselectRow - record id :' + r.id);
27110 if(!preventViewNotify){
27112 var proxy = new Roo.Element(
27113 this.grid.getRowDom(index)
27115 proxy.removeClass('bg-info info');
27117 this.fireEvent("rowdeselect", this, index);
27118 this.fireEvent("selectionchange", this);
27122 restoreLast : function(){
27124 this.last = this._last;
27129 acceptsNav : function(row, col, cm){
27130 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27134 onEditorKey : function(field, e){
27135 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27140 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27142 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27144 }else if(k == e.ENTER && !e.ctrlKey){
27148 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27150 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27152 }else if(k == e.ESC){
27156 g.startEditing(newCell[0], newCell[1]);
27162 * Ext JS Library 1.1.1
27163 * Copyright(c) 2006-2007, Ext JS, LLC.
27165 * Originally Released Under LGPL - original licence link has changed is not relivant.
27168 * <script type="text/javascript">
27172 * @class Roo.bootstrap.PagingToolbar
27173 * @extends Roo.bootstrap.NavSimplebar
27174 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27176 * Create a new PagingToolbar
27177 * @param {Object} config The config object
27178 * @param {Roo.data.Store} store
27180 Roo.bootstrap.PagingToolbar = function(config)
27182 // old args format still supported... - xtype is prefered..
27183 // created from xtype...
27185 this.ds = config.dataSource;
27187 if (config.store && !this.ds) {
27188 this.store= Roo.factory(config.store, Roo.data);
27189 this.ds = this.store;
27190 this.ds.xmodule = this.xmodule || false;
27193 this.toolbarItems = [];
27194 if (config.items) {
27195 this.toolbarItems = config.items;
27198 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27203 this.bind(this.ds);
27206 if (Roo.bootstrap.version == 4) {
27207 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27209 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27214 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27216 * @cfg {Roo.data.Store} dataSource
27217 * The underlying data store providing the paged data
27220 * @cfg {String/HTMLElement/Element} container
27221 * container The id or element that will contain the toolbar
27224 * @cfg {Boolean} displayInfo
27225 * True to display the displayMsg (defaults to false)
27228 * @cfg {Number} pageSize
27229 * The number of records to display per page (defaults to 20)
27233 * @cfg {String} displayMsg
27234 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27236 displayMsg : 'Displaying {0} - {1} of {2}',
27238 * @cfg {String} emptyMsg
27239 * The message to display when no records are found (defaults to "No data to display")
27241 emptyMsg : 'No data to display',
27243 * Customizable piece of the default paging text (defaults to "Page")
27246 beforePageText : "Page",
27248 * Customizable piece of the default paging text (defaults to "of %0")
27251 afterPageText : "of {0}",
27253 * Customizable piece of the default paging text (defaults to "First Page")
27256 firstText : "First Page",
27258 * Customizable piece of the default paging text (defaults to "Previous Page")
27261 prevText : "Previous Page",
27263 * Customizable piece of the default paging text (defaults to "Next Page")
27266 nextText : "Next Page",
27268 * Customizable piece of the default paging text (defaults to "Last Page")
27271 lastText : "Last Page",
27273 * Customizable piece of the default paging text (defaults to "Refresh")
27276 refreshText : "Refresh",
27280 onRender : function(ct, position)
27282 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27283 this.navgroup.parentId = this.id;
27284 this.navgroup.onRender(this.el, null);
27285 // add the buttons to the navgroup
27287 if(this.displayInfo){
27288 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27289 this.displayEl = this.el.select('.x-paging-info', true).first();
27290 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27291 // this.displayEl = navel.el.select('span',true).first();
27297 Roo.each(_this.buttons, function(e){ // this might need to use render????
27298 Roo.factory(e).render(_this.el);
27302 Roo.each(_this.toolbarItems, function(e) {
27303 _this.navgroup.addItem(e);
27307 this.first = this.navgroup.addItem({
27308 tooltip: this.firstText,
27309 cls: "prev btn-outline-secondary",
27310 html : ' <i class="fa fa-step-backward"></i>',
27312 preventDefault: true,
27313 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27316 this.prev = this.navgroup.addItem({
27317 tooltip: this.prevText,
27318 cls: "prev btn-outline-secondary",
27319 html : ' <i class="fa fa-backward"></i>',
27321 preventDefault: true,
27322 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27324 //this.addSeparator();
27327 var field = this.navgroup.addItem( {
27329 cls : 'x-paging-position btn-outline-secondary',
27331 html : this.beforePageText +
27332 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27333 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27336 this.field = field.el.select('input', true).first();
27337 this.field.on("keydown", this.onPagingKeydown, this);
27338 this.field.on("focus", function(){this.dom.select();});
27341 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27342 //this.field.setHeight(18);
27343 //this.addSeparator();
27344 this.next = this.navgroup.addItem({
27345 tooltip: this.nextText,
27346 cls: "next btn-outline-secondary",
27347 html : ' <i class="fa fa-forward"></i>',
27349 preventDefault: true,
27350 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27352 this.last = this.navgroup.addItem({
27353 tooltip: this.lastText,
27354 html : ' <i class="fa fa-step-forward"></i>',
27355 cls: "next btn-outline-secondary",
27357 preventDefault: true,
27358 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27360 //this.addSeparator();
27361 this.loading = this.navgroup.addItem({
27362 tooltip: this.refreshText,
27363 cls: "btn-outline-secondary",
27364 html : ' <i class="fa fa-refresh"></i>',
27365 preventDefault: true,
27366 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27372 updateInfo : function(){
27373 if(this.displayEl){
27374 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27375 var msg = count == 0 ?
27379 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27381 this.displayEl.update(msg);
27386 onLoad : function(ds, r, o)
27388 this.cursor = o.params && o.params.start ? o.params.start : 0;
27390 var d = this.getPageData(),
27395 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27396 this.field.dom.value = ap;
27397 this.first.setDisabled(ap == 1);
27398 this.prev.setDisabled(ap == 1);
27399 this.next.setDisabled(ap == ps);
27400 this.last.setDisabled(ap == ps);
27401 this.loading.enable();
27406 getPageData : function(){
27407 var total = this.ds.getTotalCount();
27410 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27411 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27416 onLoadError : function(){
27417 this.loading.enable();
27421 onPagingKeydown : function(e){
27422 var k = e.getKey();
27423 var d = this.getPageData();
27425 var v = this.field.dom.value, pageNum;
27426 if(!v || isNaN(pageNum = parseInt(v, 10))){
27427 this.field.dom.value = d.activePage;
27430 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27431 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27434 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))
27436 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27437 this.field.dom.value = pageNum;
27438 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27441 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27443 var v = this.field.dom.value, pageNum;
27444 var increment = (e.shiftKey) ? 10 : 1;
27445 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27448 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27449 this.field.dom.value = d.activePage;
27452 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27454 this.field.dom.value = parseInt(v, 10) + increment;
27455 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27456 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27463 beforeLoad : function(){
27465 this.loading.disable();
27470 onClick : function(which){
27479 ds.load({params:{start: 0, limit: this.pageSize}});
27482 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27485 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27488 var total = ds.getTotalCount();
27489 var extra = total % this.pageSize;
27490 var lastStart = extra ? (total - extra) : total-this.pageSize;
27491 ds.load({params:{start: lastStart, limit: this.pageSize}});
27494 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27500 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27501 * @param {Roo.data.Store} store The data store to unbind
27503 unbind : function(ds){
27504 ds.un("beforeload", this.beforeLoad, this);
27505 ds.un("load", this.onLoad, this);
27506 ds.un("loadexception", this.onLoadError, this);
27507 ds.un("remove", this.updateInfo, this);
27508 ds.un("add", this.updateInfo, this);
27509 this.ds = undefined;
27513 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27514 * @param {Roo.data.Store} store The data store to bind
27516 bind : function(ds){
27517 ds.on("beforeload", this.beforeLoad, this);
27518 ds.on("load", this.onLoad, this);
27519 ds.on("loadexception", this.onLoadError, this);
27520 ds.on("remove", this.updateInfo, this);
27521 ds.on("add", this.updateInfo, this);
27532 * @class Roo.bootstrap.MessageBar
27533 * @extends Roo.bootstrap.Component
27534 * Bootstrap MessageBar class
27535 * @cfg {String} html contents of the MessageBar
27536 * @cfg {String} weight (info | success | warning | danger) default info
27537 * @cfg {String} beforeClass insert the bar before the given class
27538 * @cfg {Boolean} closable (true | false) default false
27539 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27542 * Create a new Element
27543 * @param {Object} config The config object
27546 Roo.bootstrap.MessageBar = function(config){
27547 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27550 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27556 beforeClass: 'bootstrap-sticky-wrap',
27558 getAutoCreate : function(){
27562 cls: 'alert alert-dismissable alert-' + this.weight,
27567 html: this.html || ''
27573 cfg.cls += ' alert-messages-fixed';
27587 onRender : function(ct, position)
27589 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27592 var cfg = Roo.apply({}, this.getAutoCreate());
27596 cfg.cls += ' ' + this.cls;
27599 cfg.style = this.style;
27601 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27603 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27606 this.el.select('>button.close').on('click', this.hide, this);
27612 if (!this.rendered) {
27618 this.fireEvent('show', this);
27624 if (!this.rendered) {
27630 this.fireEvent('hide', this);
27633 update : function()
27635 // var e = this.el.dom.firstChild;
27637 // if(this.closable){
27638 // e = e.nextSibling;
27641 // e.data = this.html || '';
27643 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27659 * @class Roo.bootstrap.Graph
27660 * @extends Roo.bootstrap.Component
27661 * Bootstrap Graph class
27665 @cfg {String} graphtype bar | vbar | pie
27666 @cfg {number} g_x coodinator | centre x (pie)
27667 @cfg {number} g_y coodinator | centre y (pie)
27668 @cfg {number} g_r radius (pie)
27669 @cfg {number} g_height height of the chart (respected by all elements in the set)
27670 @cfg {number} g_width width of the chart (respected by all elements in the set)
27671 @cfg {Object} title The title of the chart
27674 -opts (object) options for the chart
27676 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27677 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27679 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.
27680 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27682 o stretch (boolean)
27684 -opts (object) options for the pie
27687 o startAngle (number)
27688 o endAngle (number)
27692 * Create a new Input
27693 * @param {Object} config The config object
27696 Roo.bootstrap.Graph = function(config){
27697 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27703 * The img click event for the img.
27704 * @param {Roo.EventObject} e
27710 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27721 //g_colors: this.colors,
27728 getAutoCreate : function(){
27739 onRender : function(ct,position){
27742 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27744 if (typeof(Raphael) == 'undefined') {
27745 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27749 this.raphael = Raphael(this.el.dom);
27751 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27752 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27753 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27754 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27756 r.text(160, 10, "Single Series Chart").attr(txtattr);
27757 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27758 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27759 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27761 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27762 r.barchart(330, 10, 300, 220, data1);
27763 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27764 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27767 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27768 // r.barchart(30, 30, 560, 250, xdata, {
27769 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27770 // axis : "0 0 1 1",
27771 // axisxlabels : xdata
27772 // //yvalues : cols,
27775 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27777 // this.load(null,xdata,{
27778 // axis : "0 0 1 1",
27779 // axisxlabels : xdata
27784 load : function(graphtype,xdata,opts)
27786 this.raphael.clear();
27788 graphtype = this.graphtype;
27793 var r = this.raphael,
27794 fin = function () {
27795 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27797 fout = function () {
27798 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27800 pfin = function() {
27801 this.sector.stop();
27802 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27805 this.label[0].stop();
27806 this.label[0].attr({ r: 7.5 });
27807 this.label[1].attr({ "font-weight": 800 });
27810 pfout = function() {
27811 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27814 this.label[0].animate({ r: 5 }, 500, "bounce");
27815 this.label[1].attr({ "font-weight": 400 });
27821 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27824 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27827 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27828 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27830 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27837 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27842 setTitle: function(o)
27847 initEvents: function() {
27850 this.el.on('click', this.onClick, this);
27854 onClick : function(e)
27856 Roo.log('img onclick');
27857 this.fireEvent('click', this, e);
27869 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27872 * @class Roo.bootstrap.dash.NumberBox
27873 * @extends Roo.bootstrap.Component
27874 * Bootstrap NumberBox class
27875 * @cfg {String} headline Box headline
27876 * @cfg {String} content Box content
27877 * @cfg {String} icon Box icon
27878 * @cfg {String} footer Footer text
27879 * @cfg {String} fhref Footer href
27882 * Create a new NumberBox
27883 * @param {Object} config The config object
27887 Roo.bootstrap.dash.NumberBox = function(config){
27888 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27892 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27901 getAutoCreate : function(){
27905 cls : 'small-box ',
27913 cls : 'roo-headline',
27914 html : this.headline
27918 cls : 'roo-content',
27919 html : this.content
27933 cls : 'ion ' + this.icon
27942 cls : 'small-box-footer',
27943 href : this.fhref || '#',
27947 cfg.cn.push(footer);
27954 onRender : function(ct,position){
27955 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27962 setHeadline: function (value)
27964 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27967 setFooter: function (value, href)
27969 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27972 this.el.select('a.small-box-footer',true).first().attr('href', href);
27977 setContent: function (value)
27979 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27982 initEvents: function()
27996 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27999 * @class Roo.bootstrap.dash.TabBox
28000 * @extends Roo.bootstrap.Component
28001 * Bootstrap TabBox class
28002 * @cfg {String} title Title of the TabBox
28003 * @cfg {String} icon Icon of the TabBox
28004 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28005 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28008 * Create a new TabBox
28009 * @param {Object} config The config object
28013 Roo.bootstrap.dash.TabBox = function(config){
28014 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28019 * When a pane is added
28020 * @param {Roo.bootstrap.dash.TabPane} pane
28024 * @event activatepane
28025 * When a pane is activated
28026 * @param {Roo.bootstrap.dash.TabPane} pane
28028 "activatepane" : true
28036 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28041 tabScrollable : false,
28043 getChildContainer : function()
28045 return this.el.select('.tab-content', true).first();
28048 getAutoCreate : function(){
28052 cls: 'pull-left header',
28060 cls: 'fa ' + this.icon
28066 cls: 'nav nav-tabs pull-right',
28072 if(this.tabScrollable){
28079 cls: 'nav nav-tabs pull-right',
28090 cls: 'nav-tabs-custom',
28095 cls: 'tab-content no-padding',
28103 initEvents : function()
28105 //Roo.log('add add pane handler');
28106 this.on('addpane', this.onAddPane, this);
28109 * Updates the box title
28110 * @param {String} html to set the title to.
28112 setTitle : function(value)
28114 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28116 onAddPane : function(pane)
28118 this.panes.push(pane);
28119 //Roo.log('addpane');
28121 // tabs are rendere left to right..
28122 if(!this.showtabs){
28126 var ctr = this.el.select('.nav-tabs', true).first();
28129 var existing = ctr.select('.nav-tab',true);
28130 var qty = existing.getCount();;
28133 var tab = ctr.createChild({
28135 cls : 'nav-tab' + (qty ? '' : ' active'),
28143 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28146 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28148 pane.el.addClass('active');
28153 onTabClick : function(ev,un,ob,pane)
28155 //Roo.log('tab - prev default');
28156 ev.preventDefault();
28159 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28160 pane.tab.addClass('active');
28161 //Roo.log(pane.title);
28162 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28163 // technically we should have a deactivate event.. but maybe add later.
28164 // and it should not de-activate the selected tab...
28165 this.fireEvent('activatepane', pane);
28166 pane.el.addClass('active');
28167 pane.fireEvent('activate');
28172 getActivePane : function()
28175 Roo.each(this.panes, function(p) {
28176 if(p.el.hasClass('active')){
28197 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28199 * @class Roo.bootstrap.TabPane
28200 * @extends Roo.bootstrap.Component
28201 * Bootstrap TabPane class
28202 * @cfg {Boolean} active (false | true) Default false
28203 * @cfg {String} title title of panel
28207 * Create a new TabPane
28208 * @param {Object} config The config object
28211 Roo.bootstrap.dash.TabPane = function(config){
28212 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28218 * When a pane is activated
28219 * @param {Roo.bootstrap.dash.TabPane} pane
28226 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28231 // the tabBox that this is attached to.
28234 getAutoCreate : function()
28242 cfg.cls += ' active';
28247 initEvents : function()
28249 //Roo.log('trigger add pane handler');
28250 this.parent().fireEvent('addpane', this)
28254 * Updates the tab title
28255 * @param {String} html to set the title to.
28257 setTitle: function(str)
28263 this.tab.select('a', true).first().dom.innerHTML = str;
28280 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28283 * @class Roo.bootstrap.menu.Menu
28284 * @extends Roo.bootstrap.Component
28285 * Bootstrap Menu class - container for Menu
28286 * @cfg {String} html Text of the menu
28287 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28288 * @cfg {String} icon Font awesome icon
28289 * @cfg {String} pos Menu align to (top | bottom) default bottom
28293 * Create a new Menu
28294 * @param {Object} config The config object
28298 Roo.bootstrap.menu.Menu = function(config){
28299 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28303 * @event beforeshow
28304 * Fires before this menu is displayed
28305 * @param {Roo.bootstrap.menu.Menu} this
28309 * @event beforehide
28310 * Fires before this menu is hidden
28311 * @param {Roo.bootstrap.menu.Menu} this
28316 * Fires after this menu is displayed
28317 * @param {Roo.bootstrap.menu.Menu} this
28322 * Fires after this menu is hidden
28323 * @param {Roo.bootstrap.menu.Menu} this
28328 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28329 * @param {Roo.bootstrap.menu.Menu} this
28330 * @param {Roo.EventObject} e
28337 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28341 weight : 'default',
28346 getChildContainer : function() {
28347 if(this.isSubMenu){
28351 return this.el.select('ul.dropdown-menu', true).first();
28354 getAutoCreate : function()
28359 cls : 'roo-menu-text',
28367 cls : 'fa ' + this.icon
28378 cls : 'dropdown-button btn btn-' + this.weight,
28383 cls : 'dropdown-toggle btn btn-' + this.weight,
28393 cls : 'dropdown-menu'
28399 if(this.pos == 'top'){
28400 cfg.cls += ' dropup';
28403 if(this.isSubMenu){
28406 cls : 'dropdown-menu'
28413 onRender : function(ct, position)
28415 this.isSubMenu = ct.hasClass('dropdown-submenu');
28417 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28420 initEvents : function()
28422 if(this.isSubMenu){
28426 this.hidden = true;
28428 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28429 this.triggerEl.on('click', this.onTriggerPress, this);
28431 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28432 this.buttonEl.on('click', this.onClick, this);
28438 if(this.isSubMenu){
28442 return this.el.select('ul.dropdown-menu', true).first();
28445 onClick : function(e)
28447 this.fireEvent("click", this, e);
28450 onTriggerPress : function(e)
28452 if (this.isVisible()) {
28459 isVisible : function(){
28460 return !this.hidden;
28465 this.fireEvent("beforeshow", this);
28467 this.hidden = false;
28468 this.el.addClass('open');
28470 Roo.get(document).on("mouseup", this.onMouseUp, this);
28472 this.fireEvent("show", this);
28479 this.fireEvent("beforehide", this);
28481 this.hidden = true;
28482 this.el.removeClass('open');
28484 Roo.get(document).un("mouseup", this.onMouseUp);
28486 this.fireEvent("hide", this);
28489 onMouseUp : function()
28503 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28506 * @class Roo.bootstrap.menu.Item
28507 * @extends Roo.bootstrap.Component
28508 * Bootstrap MenuItem class
28509 * @cfg {Boolean} submenu (true | false) default false
28510 * @cfg {String} html text of the item
28511 * @cfg {String} href the link
28512 * @cfg {Boolean} disable (true | false) default false
28513 * @cfg {Boolean} preventDefault (true | false) default true
28514 * @cfg {String} icon Font awesome icon
28515 * @cfg {String} pos Submenu align to (left | right) default right
28519 * Create a new Item
28520 * @param {Object} config The config object
28524 Roo.bootstrap.menu.Item = function(config){
28525 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28529 * Fires when the mouse is hovering over this menu
28530 * @param {Roo.bootstrap.menu.Item} this
28531 * @param {Roo.EventObject} e
28536 * Fires when the mouse exits this menu
28537 * @param {Roo.bootstrap.menu.Item} this
28538 * @param {Roo.EventObject} e
28544 * The raw click event for the entire grid.
28545 * @param {Roo.EventObject} e
28551 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28556 preventDefault: true,
28561 getAutoCreate : function()
28566 cls : 'roo-menu-item-text',
28574 cls : 'fa ' + this.icon
28583 href : this.href || '#',
28590 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28594 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28596 if(this.pos == 'left'){
28597 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28604 initEvents : function()
28606 this.el.on('mouseover', this.onMouseOver, this);
28607 this.el.on('mouseout', this.onMouseOut, this);
28609 this.el.select('a', true).first().on('click', this.onClick, this);
28613 onClick : function(e)
28615 if(this.preventDefault){
28616 e.preventDefault();
28619 this.fireEvent("click", this, e);
28622 onMouseOver : function(e)
28624 if(this.submenu && this.pos == 'left'){
28625 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28628 this.fireEvent("mouseover", this, e);
28631 onMouseOut : function(e)
28633 this.fireEvent("mouseout", this, e);
28645 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28648 * @class Roo.bootstrap.menu.Separator
28649 * @extends Roo.bootstrap.Component
28650 * Bootstrap Separator class
28653 * Create a new Separator
28654 * @param {Object} config The config object
28658 Roo.bootstrap.menu.Separator = function(config){
28659 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28662 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28664 getAutoCreate : function(){
28685 * @class Roo.bootstrap.Tooltip
28686 * Bootstrap Tooltip class
28687 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28688 * to determine which dom element triggers the tooltip.
28690 * It needs to add support for additional attributes like tooltip-position
28693 * Create a new Toolti
28694 * @param {Object} config The config object
28697 Roo.bootstrap.Tooltip = function(config){
28698 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28700 this.alignment = Roo.bootstrap.Tooltip.alignment;
28702 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28703 this.alignment = config.alignment;
28708 Roo.apply(Roo.bootstrap.Tooltip, {
28710 * @function init initialize tooltip monitoring.
28714 currentTip : false,
28715 currentRegion : false,
28721 Roo.get(document).on('mouseover', this.enter ,this);
28722 Roo.get(document).on('mouseout', this.leave, this);
28725 this.currentTip = new Roo.bootstrap.Tooltip();
28728 enter : function(ev)
28730 var dom = ev.getTarget();
28732 //Roo.log(['enter',dom]);
28733 var el = Roo.fly(dom);
28734 if (this.currentEl) {
28736 //Roo.log(this.currentEl);
28737 //Roo.log(this.currentEl.contains(dom));
28738 if (this.currentEl == el) {
28741 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28747 if (this.currentTip.el) {
28748 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28752 if(!el || el.dom == document){
28758 // you can not look for children, as if el is the body.. then everythign is the child..
28759 if (!el.attr('tooltip')) { //
28760 if (!el.select("[tooltip]").elements.length) {
28763 // is the mouse over this child...?
28764 bindEl = el.select("[tooltip]").first();
28765 var xy = ev.getXY();
28766 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28767 //Roo.log("not in region.");
28770 //Roo.log("child element over..");
28773 this.currentEl = bindEl;
28774 this.currentTip.bind(bindEl);
28775 this.currentRegion = Roo.lib.Region.getRegion(dom);
28776 this.currentTip.enter();
28779 leave : function(ev)
28781 var dom = ev.getTarget();
28782 //Roo.log(['leave',dom]);
28783 if (!this.currentEl) {
28788 if (dom != this.currentEl.dom) {
28791 var xy = ev.getXY();
28792 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28795 // only activate leave if mouse cursor is outside... bounding box..
28800 if (this.currentTip) {
28801 this.currentTip.leave();
28803 //Roo.log('clear currentEl');
28804 this.currentEl = false;
28809 'left' : ['r-l', [-2,0], 'right'],
28810 'right' : ['l-r', [2,0], 'left'],
28811 'bottom' : ['t-b', [0,2], 'top'],
28812 'top' : [ 'b-t', [0,-2], 'bottom']
28818 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28823 delay : null, // can be { show : 300 , hide: 500}
28827 hoverState : null, //???
28829 placement : 'bottom',
28833 getAutoCreate : function(){
28840 cls : 'tooltip-arrow arrow'
28843 cls : 'tooltip-inner'
28850 bind : function(el)
28855 initEvents : function()
28857 this.arrowEl = this.el.select('.arrow', true).first();
28858 this.innerEl = this.el.select('.tooltip-inner', true).first();
28861 enter : function () {
28863 if (this.timeout != null) {
28864 clearTimeout(this.timeout);
28867 this.hoverState = 'in';
28868 //Roo.log("enter - show");
28869 if (!this.delay || !this.delay.show) {
28874 this.timeout = setTimeout(function () {
28875 if (_t.hoverState == 'in') {
28878 }, this.delay.show);
28882 clearTimeout(this.timeout);
28884 this.hoverState = 'out';
28885 if (!this.delay || !this.delay.hide) {
28891 this.timeout = setTimeout(function () {
28892 //Roo.log("leave - timeout");
28894 if (_t.hoverState == 'out') {
28896 Roo.bootstrap.Tooltip.currentEl = false;
28901 show : function (msg)
28904 this.render(document.body);
28907 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28909 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28911 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28913 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28914 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28916 var placement = typeof this.placement == 'function' ?
28917 this.placement.call(this, this.el, on_el) :
28920 var autoToken = /\s?auto?\s?/i;
28921 var autoPlace = autoToken.test(placement);
28923 placement = placement.replace(autoToken, '') || 'top';
28927 //this.el.setXY([0,0]);
28929 //this.el.dom.style.display='block';
28931 //this.el.appendTo(on_el);
28933 var p = this.getPosition();
28934 var box = this.el.getBox();
28940 var align = this.alignment[placement];
28942 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28944 if(placement == 'top' || placement == 'bottom'){
28946 placement = 'right';
28949 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28950 placement = 'left';
28953 var scroll = Roo.select('body', true).first().getScroll();
28955 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28959 align = this.alignment[placement];
28961 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28965 this.el.alignTo(this.bindEl, align[0],align[1]);
28966 //var arrow = this.el.select('.arrow',true).first();
28967 //arrow.set(align[2],
28969 this.el.addClass(placement);
28970 this.el.addClass("bs-tooltip-"+ placement);
28972 this.el.addClass('in fade show');
28974 this.hoverState = null;
28976 if (this.el.hasClass('fade')) {
28991 //this.el.setXY([0,0]);
28992 this.el.removeClass(['show', 'in']);
29008 * @class Roo.bootstrap.LocationPicker
29009 * @extends Roo.bootstrap.Component
29010 * Bootstrap LocationPicker class
29011 * @cfg {Number} latitude Position when init default 0
29012 * @cfg {Number} longitude Position when init default 0
29013 * @cfg {Number} zoom default 15
29014 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29015 * @cfg {Boolean} mapTypeControl default false
29016 * @cfg {Boolean} disableDoubleClickZoom default false
29017 * @cfg {Boolean} scrollwheel default true
29018 * @cfg {Boolean} streetViewControl default false
29019 * @cfg {Number} radius default 0
29020 * @cfg {String} locationName
29021 * @cfg {Boolean} draggable default true
29022 * @cfg {Boolean} enableAutocomplete default false
29023 * @cfg {Boolean} enableReverseGeocode default true
29024 * @cfg {String} markerTitle
29027 * Create a new LocationPicker
29028 * @param {Object} config The config object
29032 Roo.bootstrap.LocationPicker = function(config){
29034 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29039 * Fires when the picker initialized.
29040 * @param {Roo.bootstrap.LocationPicker} this
29041 * @param {Google Location} location
29045 * @event positionchanged
29046 * Fires when the picker position changed.
29047 * @param {Roo.bootstrap.LocationPicker} this
29048 * @param {Google Location} location
29050 positionchanged : true,
29053 * Fires when the map resize.
29054 * @param {Roo.bootstrap.LocationPicker} this
29059 * Fires when the map show.
29060 * @param {Roo.bootstrap.LocationPicker} this
29065 * Fires when the map hide.
29066 * @param {Roo.bootstrap.LocationPicker} this
29071 * Fires when click the map.
29072 * @param {Roo.bootstrap.LocationPicker} this
29073 * @param {Map event} e
29077 * @event mapRightClick
29078 * Fires when right click the map.
29079 * @param {Roo.bootstrap.LocationPicker} this
29080 * @param {Map event} e
29082 mapRightClick : true,
29084 * @event markerClick
29085 * Fires when click the marker.
29086 * @param {Roo.bootstrap.LocationPicker} this
29087 * @param {Map event} e
29089 markerClick : true,
29091 * @event markerRightClick
29092 * Fires when right click the marker.
29093 * @param {Roo.bootstrap.LocationPicker} this
29094 * @param {Map event} e
29096 markerRightClick : true,
29098 * @event OverlayViewDraw
29099 * Fires when OverlayView Draw
29100 * @param {Roo.bootstrap.LocationPicker} this
29102 OverlayViewDraw : true,
29104 * @event OverlayViewOnAdd
29105 * Fires when OverlayView Draw
29106 * @param {Roo.bootstrap.LocationPicker} this
29108 OverlayViewOnAdd : true,
29110 * @event OverlayViewOnRemove
29111 * Fires when OverlayView Draw
29112 * @param {Roo.bootstrap.LocationPicker} this
29114 OverlayViewOnRemove : true,
29116 * @event OverlayViewShow
29117 * Fires when OverlayView Draw
29118 * @param {Roo.bootstrap.LocationPicker} this
29119 * @param {Pixel} cpx
29121 OverlayViewShow : true,
29123 * @event OverlayViewHide
29124 * Fires when OverlayView Draw
29125 * @param {Roo.bootstrap.LocationPicker} this
29127 OverlayViewHide : true,
29129 * @event loadexception
29130 * Fires when load google lib failed.
29131 * @param {Roo.bootstrap.LocationPicker} this
29133 loadexception : true
29138 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29140 gMapContext: false,
29146 mapTypeControl: false,
29147 disableDoubleClickZoom: false,
29149 streetViewControl: false,
29153 enableAutocomplete: false,
29154 enableReverseGeocode: true,
29157 getAutoCreate: function()
29162 cls: 'roo-location-picker'
29168 initEvents: function(ct, position)
29170 if(!this.el.getWidth() || this.isApplied()){
29174 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29179 initial: function()
29181 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29182 this.fireEvent('loadexception', this);
29186 if(!this.mapTypeId){
29187 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29190 this.gMapContext = this.GMapContext();
29192 this.initOverlayView();
29194 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29198 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29199 _this.setPosition(_this.gMapContext.marker.position);
29202 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29203 _this.fireEvent('mapClick', this, event);
29207 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29208 _this.fireEvent('mapRightClick', this, event);
29212 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29213 _this.fireEvent('markerClick', this, event);
29217 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29218 _this.fireEvent('markerRightClick', this, event);
29222 this.setPosition(this.gMapContext.location);
29224 this.fireEvent('initial', this, this.gMapContext.location);
29227 initOverlayView: function()
29231 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29235 _this.fireEvent('OverlayViewDraw', _this);
29240 _this.fireEvent('OverlayViewOnAdd', _this);
29243 onRemove: function()
29245 _this.fireEvent('OverlayViewOnRemove', _this);
29248 show: function(cpx)
29250 _this.fireEvent('OverlayViewShow', _this, cpx);
29255 _this.fireEvent('OverlayViewHide', _this);
29261 fromLatLngToContainerPixel: function(event)
29263 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29266 isApplied: function()
29268 return this.getGmapContext() == false ? false : true;
29271 getGmapContext: function()
29273 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29276 GMapContext: function()
29278 var position = new google.maps.LatLng(this.latitude, this.longitude);
29280 var _map = new google.maps.Map(this.el.dom, {
29283 mapTypeId: this.mapTypeId,
29284 mapTypeControl: this.mapTypeControl,
29285 disableDoubleClickZoom: this.disableDoubleClickZoom,
29286 scrollwheel: this.scrollwheel,
29287 streetViewControl: this.streetViewControl,
29288 locationName: this.locationName,
29289 draggable: this.draggable,
29290 enableAutocomplete: this.enableAutocomplete,
29291 enableReverseGeocode: this.enableReverseGeocode
29294 var _marker = new google.maps.Marker({
29295 position: position,
29297 title: this.markerTitle,
29298 draggable: this.draggable
29305 location: position,
29306 radius: this.radius,
29307 locationName: this.locationName,
29308 addressComponents: {
29309 formatted_address: null,
29310 addressLine1: null,
29311 addressLine2: null,
29313 streetNumber: null,
29317 stateOrProvince: null
29320 domContainer: this.el.dom,
29321 geodecoder: new google.maps.Geocoder()
29325 drawCircle: function(center, radius, options)
29327 if (this.gMapContext.circle != null) {
29328 this.gMapContext.circle.setMap(null);
29332 options = Roo.apply({}, options, {
29333 strokeColor: "#0000FF",
29334 strokeOpacity: .35,
29336 fillColor: "#0000FF",
29340 options.map = this.gMapContext.map;
29341 options.radius = radius;
29342 options.center = center;
29343 this.gMapContext.circle = new google.maps.Circle(options);
29344 return this.gMapContext.circle;
29350 setPosition: function(location)
29352 this.gMapContext.location = location;
29353 this.gMapContext.marker.setPosition(location);
29354 this.gMapContext.map.panTo(location);
29355 this.drawCircle(location, this.gMapContext.radius, {});
29359 if (this.gMapContext.settings.enableReverseGeocode) {
29360 this.gMapContext.geodecoder.geocode({
29361 latLng: this.gMapContext.location
29362 }, function(results, status) {
29364 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29365 _this.gMapContext.locationName = results[0].formatted_address;
29366 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29368 _this.fireEvent('positionchanged', this, location);
29375 this.fireEvent('positionchanged', this, location);
29380 google.maps.event.trigger(this.gMapContext.map, "resize");
29382 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29384 this.fireEvent('resize', this);
29387 setPositionByLatLng: function(latitude, longitude)
29389 this.setPosition(new google.maps.LatLng(latitude, longitude));
29392 getCurrentPosition: function()
29395 latitude: this.gMapContext.location.lat(),
29396 longitude: this.gMapContext.location.lng()
29400 getAddressName: function()
29402 return this.gMapContext.locationName;
29405 getAddressComponents: function()
29407 return this.gMapContext.addressComponents;
29410 address_component_from_google_geocode: function(address_components)
29414 for (var i = 0; i < address_components.length; i++) {
29415 var component = address_components[i];
29416 if (component.types.indexOf("postal_code") >= 0) {
29417 result.postalCode = component.short_name;
29418 } else if (component.types.indexOf("street_number") >= 0) {
29419 result.streetNumber = component.short_name;
29420 } else if (component.types.indexOf("route") >= 0) {
29421 result.streetName = component.short_name;
29422 } else if (component.types.indexOf("neighborhood") >= 0) {
29423 result.city = component.short_name;
29424 } else if (component.types.indexOf("locality") >= 0) {
29425 result.city = component.short_name;
29426 } else if (component.types.indexOf("sublocality") >= 0) {
29427 result.district = component.short_name;
29428 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29429 result.stateOrProvince = component.short_name;
29430 } else if (component.types.indexOf("country") >= 0) {
29431 result.country = component.short_name;
29435 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29436 result.addressLine2 = "";
29440 setZoomLevel: function(zoom)
29442 this.gMapContext.map.setZoom(zoom);
29455 this.fireEvent('show', this);
29466 this.fireEvent('hide', this);
29471 Roo.apply(Roo.bootstrap.LocationPicker, {
29473 OverlayView : function(map, options)
29475 options = options || {};
29482 * @class Roo.bootstrap.Alert
29483 * @extends Roo.bootstrap.Component
29484 * Bootstrap Alert class - shows an alert area box
29486 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29487 Enter a valid email address
29490 * @cfg {String} title The title of alert
29491 * @cfg {String} html The content of alert
29492 * @cfg {String} weight ( success | info | warning | danger )
29493 * @cfg {String} faicon font-awesomeicon
29496 * Create a new alert
29497 * @param {Object} config The config object
29501 Roo.bootstrap.Alert = function(config){
29502 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29506 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29513 getAutoCreate : function()
29522 cls : 'roo-alert-icon'
29527 cls : 'roo-alert-title',
29532 cls : 'roo-alert-text',
29539 cfg.cn[0].cls += ' fa ' + this.faicon;
29543 cfg.cls += ' alert-' + this.weight;
29549 initEvents: function()
29551 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29554 setTitle : function(str)
29556 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29559 setText : function(str)
29561 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29564 setWeight : function(weight)
29567 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29570 this.weight = weight;
29572 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29575 setIcon : function(icon)
29578 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29581 this.faicon = icon;
29583 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29604 * @class Roo.bootstrap.UploadCropbox
29605 * @extends Roo.bootstrap.Component
29606 * Bootstrap UploadCropbox class
29607 * @cfg {String} emptyText show when image has been loaded
29608 * @cfg {String} rotateNotify show when image too small to rotate
29609 * @cfg {Number} errorTimeout default 3000
29610 * @cfg {Number} minWidth default 300
29611 * @cfg {Number} minHeight default 300
29612 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29613 * @cfg {Boolean} isDocument (true|false) default false
29614 * @cfg {String} url action url
29615 * @cfg {String} paramName default 'imageUpload'
29616 * @cfg {String} method default POST
29617 * @cfg {Boolean} loadMask (true|false) default true
29618 * @cfg {Boolean} loadingText default 'Loading...'
29621 * Create a new UploadCropbox
29622 * @param {Object} config The config object
29625 Roo.bootstrap.UploadCropbox = function(config){
29626 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29630 * @event beforeselectfile
29631 * Fire before select file
29632 * @param {Roo.bootstrap.UploadCropbox} this
29634 "beforeselectfile" : true,
29637 * Fire after initEvent
29638 * @param {Roo.bootstrap.UploadCropbox} this
29643 * Fire after initEvent
29644 * @param {Roo.bootstrap.UploadCropbox} this
29645 * @param {String} data
29650 * Fire when preparing the file data
29651 * @param {Roo.bootstrap.UploadCropbox} this
29652 * @param {Object} file
29657 * Fire when get exception
29658 * @param {Roo.bootstrap.UploadCropbox} this
29659 * @param {XMLHttpRequest} xhr
29661 "exception" : true,
29663 * @event beforeloadcanvas
29664 * Fire before load the canvas
29665 * @param {Roo.bootstrap.UploadCropbox} this
29666 * @param {String} src
29668 "beforeloadcanvas" : true,
29671 * Fire when trash image
29672 * @param {Roo.bootstrap.UploadCropbox} this
29677 * Fire when download the image
29678 * @param {Roo.bootstrap.UploadCropbox} this
29682 * @event footerbuttonclick
29683 * Fire when footerbuttonclick
29684 * @param {Roo.bootstrap.UploadCropbox} this
29685 * @param {String} type
29687 "footerbuttonclick" : true,
29691 * @param {Roo.bootstrap.UploadCropbox} this
29696 * Fire when rotate the image
29697 * @param {Roo.bootstrap.UploadCropbox} this
29698 * @param {String} pos
29703 * Fire when inspect the file
29704 * @param {Roo.bootstrap.UploadCropbox} this
29705 * @param {Object} file
29710 * Fire when xhr upload the file
29711 * @param {Roo.bootstrap.UploadCropbox} this
29712 * @param {Object} data
29717 * Fire when arrange the file data
29718 * @param {Roo.bootstrap.UploadCropbox} this
29719 * @param {Object} formData
29724 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29727 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29729 emptyText : 'Click to upload image',
29730 rotateNotify : 'Image is too small to rotate',
29731 errorTimeout : 3000,
29745 cropType : 'image/jpeg',
29747 canvasLoaded : false,
29748 isDocument : false,
29750 paramName : 'imageUpload',
29752 loadingText : 'Loading...',
29755 getAutoCreate : function()
29759 cls : 'roo-upload-cropbox',
29763 cls : 'roo-upload-cropbox-selector',
29768 cls : 'roo-upload-cropbox-body',
29769 style : 'cursor:pointer',
29773 cls : 'roo-upload-cropbox-preview'
29777 cls : 'roo-upload-cropbox-thumb'
29781 cls : 'roo-upload-cropbox-empty-notify',
29782 html : this.emptyText
29786 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29787 html : this.rotateNotify
29793 cls : 'roo-upload-cropbox-footer',
29796 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29806 onRender : function(ct, position)
29808 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29810 if (this.buttons.length) {
29812 Roo.each(this.buttons, function(bb) {
29814 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29816 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29822 this.maskEl = this.el;
29826 initEvents : function()
29828 this.urlAPI = (window.createObjectURL && window) ||
29829 (window.URL && URL.revokeObjectURL && URL) ||
29830 (window.webkitURL && webkitURL);
29832 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29833 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29835 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29836 this.selectorEl.hide();
29838 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29839 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29841 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29842 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29843 this.thumbEl.hide();
29845 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29846 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29848 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29849 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29850 this.errorEl.hide();
29852 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29853 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29854 this.footerEl.hide();
29856 this.setThumbBoxSize();
29862 this.fireEvent('initial', this);
29869 window.addEventListener("resize", function() { _this.resize(); } );
29871 this.bodyEl.on('click', this.beforeSelectFile, this);
29874 this.bodyEl.on('touchstart', this.onTouchStart, this);
29875 this.bodyEl.on('touchmove', this.onTouchMove, this);
29876 this.bodyEl.on('touchend', this.onTouchEnd, this);
29880 this.bodyEl.on('mousedown', this.onMouseDown, this);
29881 this.bodyEl.on('mousemove', this.onMouseMove, this);
29882 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29883 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29884 Roo.get(document).on('mouseup', this.onMouseUp, this);
29887 this.selectorEl.on('change', this.onFileSelected, this);
29893 this.baseScale = 1;
29895 this.baseRotate = 1;
29896 this.dragable = false;
29897 this.pinching = false;
29900 this.cropData = false;
29901 this.notifyEl.dom.innerHTML = this.emptyText;
29903 this.selectorEl.dom.value = '';
29907 resize : function()
29909 if(this.fireEvent('resize', this) != false){
29910 this.setThumbBoxPosition();
29911 this.setCanvasPosition();
29915 onFooterButtonClick : function(e, el, o, type)
29918 case 'rotate-left' :
29919 this.onRotateLeft(e);
29921 case 'rotate-right' :
29922 this.onRotateRight(e);
29925 this.beforeSelectFile(e);
29940 this.fireEvent('footerbuttonclick', this, type);
29943 beforeSelectFile : function(e)
29945 e.preventDefault();
29947 if(this.fireEvent('beforeselectfile', this) != false){
29948 this.selectorEl.dom.click();
29952 onFileSelected : function(e)
29954 e.preventDefault();
29956 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29960 var file = this.selectorEl.dom.files[0];
29962 if(this.fireEvent('inspect', this, file) != false){
29963 this.prepare(file);
29968 trash : function(e)
29970 this.fireEvent('trash', this);
29973 download : function(e)
29975 this.fireEvent('download', this);
29978 loadCanvas : function(src)
29980 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29984 this.imageEl = document.createElement('img');
29988 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29990 this.imageEl.src = src;
29994 onLoadCanvas : function()
29996 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29997 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29999 this.bodyEl.un('click', this.beforeSelectFile, this);
30001 this.notifyEl.hide();
30002 this.thumbEl.show();
30003 this.footerEl.show();
30005 this.baseRotateLevel();
30007 if(this.isDocument){
30008 this.setThumbBoxSize();
30011 this.setThumbBoxPosition();
30013 this.baseScaleLevel();
30019 this.canvasLoaded = true;
30022 this.maskEl.unmask();
30027 setCanvasPosition : function()
30029 if(!this.canvasEl){
30033 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30034 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30036 this.previewEl.setLeft(pw);
30037 this.previewEl.setTop(ph);
30041 onMouseDown : function(e)
30045 this.dragable = true;
30046 this.pinching = false;
30048 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30049 this.dragable = false;
30053 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30054 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30058 onMouseMove : function(e)
30062 if(!this.canvasLoaded){
30066 if (!this.dragable){
30070 var minX = Math.ceil(this.thumbEl.getLeft(true));
30071 var minY = Math.ceil(this.thumbEl.getTop(true));
30073 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30074 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30076 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30077 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30079 x = x - this.mouseX;
30080 y = y - this.mouseY;
30082 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30083 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30085 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30086 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30088 this.previewEl.setLeft(bgX);
30089 this.previewEl.setTop(bgY);
30091 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30092 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30095 onMouseUp : function(e)
30099 this.dragable = false;
30102 onMouseWheel : function(e)
30106 this.startScale = this.scale;
30108 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30110 if(!this.zoomable()){
30111 this.scale = this.startScale;
30120 zoomable : function()
30122 var minScale = this.thumbEl.getWidth() / this.minWidth;
30124 if(this.minWidth < this.minHeight){
30125 minScale = this.thumbEl.getHeight() / this.minHeight;
30128 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30129 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30133 (this.rotate == 0 || this.rotate == 180) &&
30135 width > this.imageEl.OriginWidth ||
30136 height > this.imageEl.OriginHeight ||
30137 (width < this.minWidth && height < this.minHeight)
30145 (this.rotate == 90 || this.rotate == 270) &&
30147 width > this.imageEl.OriginWidth ||
30148 height > this.imageEl.OriginHeight ||
30149 (width < this.minHeight && height < this.minWidth)
30156 !this.isDocument &&
30157 (this.rotate == 0 || this.rotate == 180) &&
30159 width < this.minWidth ||
30160 width > this.imageEl.OriginWidth ||
30161 height < this.minHeight ||
30162 height > this.imageEl.OriginHeight
30169 !this.isDocument &&
30170 (this.rotate == 90 || this.rotate == 270) &&
30172 width < this.minHeight ||
30173 width > this.imageEl.OriginWidth ||
30174 height < this.minWidth ||
30175 height > this.imageEl.OriginHeight
30185 onRotateLeft : function(e)
30187 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30189 var minScale = this.thumbEl.getWidth() / this.minWidth;
30191 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30192 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30194 this.startScale = this.scale;
30196 while (this.getScaleLevel() < minScale){
30198 this.scale = this.scale + 1;
30200 if(!this.zoomable()){
30205 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30206 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30211 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30218 this.scale = this.startScale;
30220 this.onRotateFail();
30225 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30227 if(this.isDocument){
30228 this.setThumbBoxSize();
30229 this.setThumbBoxPosition();
30230 this.setCanvasPosition();
30235 this.fireEvent('rotate', this, 'left');
30239 onRotateRight : function(e)
30241 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30243 var minScale = this.thumbEl.getWidth() / this.minWidth;
30245 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30246 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30248 this.startScale = this.scale;
30250 while (this.getScaleLevel() < minScale){
30252 this.scale = this.scale + 1;
30254 if(!this.zoomable()){
30259 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30260 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30265 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30272 this.scale = this.startScale;
30274 this.onRotateFail();
30279 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30281 if(this.isDocument){
30282 this.setThumbBoxSize();
30283 this.setThumbBoxPosition();
30284 this.setCanvasPosition();
30289 this.fireEvent('rotate', this, 'right');
30292 onRotateFail : function()
30294 this.errorEl.show(true);
30298 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30303 this.previewEl.dom.innerHTML = '';
30305 var canvasEl = document.createElement("canvas");
30307 var contextEl = canvasEl.getContext("2d");
30309 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30310 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30311 var center = this.imageEl.OriginWidth / 2;
30313 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30314 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30315 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30316 center = this.imageEl.OriginHeight / 2;
30319 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30321 contextEl.translate(center, center);
30322 contextEl.rotate(this.rotate * Math.PI / 180);
30324 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30326 this.canvasEl = document.createElement("canvas");
30328 this.contextEl = this.canvasEl.getContext("2d");
30330 switch (this.rotate) {
30333 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30334 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30336 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30341 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30342 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30344 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30345 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);
30349 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30354 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30355 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30357 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30358 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);
30362 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);
30367 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30368 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30370 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30371 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30375 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);
30382 this.previewEl.appendChild(this.canvasEl);
30384 this.setCanvasPosition();
30389 if(!this.canvasLoaded){
30393 var imageCanvas = document.createElement("canvas");
30395 var imageContext = imageCanvas.getContext("2d");
30397 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30398 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30400 var center = imageCanvas.width / 2;
30402 imageContext.translate(center, center);
30404 imageContext.rotate(this.rotate * Math.PI / 180);
30406 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30408 var canvas = document.createElement("canvas");
30410 var context = canvas.getContext("2d");
30412 canvas.width = this.minWidth;
30413 canvas.height = this.minHeight;
30415 switch (this.rotate) {
30418 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30419 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30421 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30422 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30424 var targetWidth = this.minWidth - 2 * x;
30425 var targetHeight = this.minHeight - 2 * y;
30429 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30430 scale = targetWidth / width;
30433 if(x > 0 && y == 0){
30434 scale = targetHeight / height;
30437 if(x > 0 && y > 0){
30438 scale = targetWidth / width;
30440 if(width < height){
30441 scale = targetHeight / height;
30445 context.scale(scale, scale);
30447 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30448 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30450 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30451 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30453 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30458 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30459 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30461 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30462 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30464 var targetWidth = this.minWidth - 2 * x;
30465 var targetHeight = this.minHeight - 2 * y;
30469 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30470 scale = targetWidth / width;
30473 if(x > 0 && y == 0){
30474 scale = targetHeight / height;
30477 if(x > 0 && y > 0){
30478 scale = targetWidth / width;
30480 if(width < height){
30481 scale = targetHeight / height;
30485 context.scale(scale, scale);
30487 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30488 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30490 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30491 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30493 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30495 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30500 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30501 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30503 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30504 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30506 var targetWidth = this.minWidth - 2 * x;
30507 var targetHeight = this.minHeight - 2 * y;
30511 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30512 scale = targetWidth / width;
30515 if(x > 0 && y == 0){
30516 scale = targetHeight / height;
30519 if(x > 0 && y > 0){
30520 scale = targetWidth / width;
30522 if(width < height){
30523 scale = targetHeight / height;
30527 context.scale(scale, scale);
30529 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30530 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30532 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30533 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30535 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30536 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30538 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30543 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30544 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30546 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30547 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30549 var targetWidth = this.minWidth - 2 * x;
30550 var targetHeight = this.minHeight - 2 * y;
30554 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30555 scale = targetWidth / width;
30558 if(x > 0 && y == 0){
30559 scale = targetHeight / height;
30562 if(x > 0 && y > 0){
30563 scale = targetWidth / width;
30565 if(width < height){
30566 scale = targetHeight / height;
30570 context.scale(scale, scale);
30572 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30573 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30575 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30576 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30578 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30580 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30587 this.cropData = canvas.toDataURL(this.cropType);
30589 if(this.fireEvent('crop', this, this.cropData) !== false){
30590 this.process(this.file, this.cropData);
30597 setThumbBoxSize : function()
30601 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30602 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30603 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30605 this.minWidth = width;
30606 this.minHeight = height;
30608 if(this.rotate == 90 || this.rotate == 270){
30609 this.minWidth = height;
30610 this.minHeight = width;
30615 width = Math.ceil(this.minWidth * height / this.minHeight);
30617 if(this.minWidth > this.minHeight){
30619 height = Math.ceil(this.minHeight * width / this.minWidth);
30622 this.thumbEl.setStyle({
30623 width : width + 'px',
30624 height : height + 'px'
30631 setThumbBoxPosition : function()
30633 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30634 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30636 this.thumbEl.setLeft(x);
30637 this.thumbEl.setTop(y);
30641 baseRotateLevel : function()
30643 this.baseRotate = 1;
30646 typeof(this.exif) != 'undefined' &&
30647 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30648 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30650 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30653 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30657 baseScaleLevel : function()
30661 if(this.isDocument){
30663 if(this.baseRotate == 6 || this.baseRotate == 8){
30665 height = this.thumbEl.getHeight();
30666 this.baseScale = height / this.imageEl.OriginWidth;
30668 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30669 width = this.thumbEl.getWidth();
30670 this.baseScale = width / this.imageEl.OriginHeight;
30676 height = this.thumbEl.getHeight();
30677 this.baseScale = height / this.imageEl.OriginHeight;
30679 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30680 width = this.thumbEl.getWidth();
30681 this.baseScale = width / this.imageEl.OriginWidth;
30687 if(this.baseRotate == 6 || this.baseRotate == 8){
30689 width = this.thumbEl.getHeight();
30690 this.baseScale = width / this.imageEl.OriginHeight;
30692 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30693 height = this.thumbEl.getWidth();
30694 this.baseScale = height / this.imageEl.OriginHeight;
30697 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30698 height = this.thumbEl.getWidth();
30699 this.baseScale = height / this.imageEl.OriginHeight;
30701 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30702 width = this.thumbEl.getHeight();
30703 this.baseScale = width / this.imageEl.OriginWidth;
30710 width = this.thumbEl.getWidth();
30711 this.baseScale = width / this.imageEl.OriginWidth;
30713 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30714 height = this.thumbEl.getHeight();
30715 this.baseScale = height / this.imageEl.OriginHeight;
30718 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30720 height = this.thumbEl.getHeight();
30721 this.baseScale = height / this.imageEl.OriginHeight;
30723 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30724 width = this.thumbEl.getWidth();
30725 this.baseScale = width / this.imageEl.OriginWidth;
30733 getScaleLevel : function()
30735 return this.baseScale * Math.pow(1.1, this.scale);
30738 onTouchStart : function(e)
30740 if(!this.canvasLoaded){
30741 this.beforeSelectFile(e);
30745 var touches = e.browserEvent.touches;
30751 if(touches.length == 1){
30752 this.onMouseDown(e);
30756 if(touches.length != 2){
30762 for(var i = 0, finger; finger = touches[i]; i++){
30763 coords.push(finger.pageX, finger.pageY);
30766 var x = Math.pow(coords[0] - coords[2], 2);
30767 var y = Math.pow(coords[1] - coords[3], 2);
30769 this.startDistance = Math.sqrt(x + y);
30771 this.startScale = this.scale;
30773 this.pinching = true;
30774 this.dragable = false;
30778 onTouchMove : function(e)
30780 if(!this.pinching && !this.dragable){
30784 var touches = e.browserEvent.touches;
30791 this.onMouseMove(e);
30797 for(var i = 0, finger; finger = touches[i]; i++){
30798 coords.push(finger.pageX, finger.pageY);
30801 var x = Math.pow(coords[0] - coords[2], 2);
30802 var y = Math.pow(coords[1] - coords[3], 2);
30804 this.endDistance = Math.sqrt(x + y);
30806 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30808 if(!this.zoomable()){
30809 this.scale = this.startScale;
30817 onTouchEnd : function(e)
30819 this.pinching = false;
30820 this.dragable = false;
30824 process : function(file, crop)
30827 this.maskEl.mask(this.loadingText);
30830 this.xhr = new XMLHttpRequest();
30832 file.xhr = this.xhr;
30834 this.xhr.open(this.method, this.url, true);
30837 "Accept": "application/json",
30838 "Cache-Control": "no-cache",
30839 "X-Requested-With": "XMLHttpRequest"
30842 for (var headerName in headers) {
30843 var headerValue = headers[headerName];
30845 this.xhr.setRequestHeader(headerName, headerValue);
30851 this.xhr.onload = function()
30853 _this.xhrOnLoad(_this.xhr);
30856 this.xhr.onerror = function()
30858 _this.xhrOnError(_this.xhr);
30861 var formData = new FormData();
30863 formData.append('returnHTML', 'NO');
30866 formData.append('crop', crop);
30869 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30870 formData.append(this.paramName, file, file.name);
30873 if(typeof(file.filename) != 'undefined'){
30874 formData.append('filename', file.filename);
30877 if(typeof(file.mimetype) != 'undefined'){
30878 formData.append('mimetype', file.mimetype);
30881 if(this.fireEvent('arrange', this, formData) != false){
30882 this.xhr.send(formData);
30886 xhrOnLoad : function(xhr)
30889 this.maskEl.unmask();
30892 if (xhr.readyState !== 4) {
30893 this.fireEvent('exception', this, xhr);
30897 var response = Roo.decode(xhr.responseText);
30899 if(!response.success){
30900 this.fireEvent('exception', this, xhr);
30904 var response = Roo.decode(xhr.responseText);
30906 this.fireEvent('upload', this, response);
30910 xhrOnError : function()
30913 this.maskEl.unmask();
30916 Roo.log('xhr on error');
30918 var response = Roo.decode(xhr.responseText);
30924 prepare : function(file)
30927 this.maskEl.mask(this.loadingText);
30933 if(typeof(file) === 'string'){
30934 this.loadCanvas(file);
30938 if(!file || !this.urlAPI){
30943 this.cropType = file.type;
30947 if(this.fireEvent('prepare', this, this.file) != false){
30949 var reader = new FileReader();
30951 reader.onload = function (e) {
30952 if (e.target.error) {
30953 Roo.log(e.target.error);
30957 var buffer = e.target.result,
30958 dataView = new DataView(buffer),
30960 maxOffset = dataView.byteLength - 4,
30964 if (dataView.getUint16(0) === 0xffd8) {
30965 while (offset < maxOffset) {
30966 markerBytes = dataView.getUint16(offset);
30968 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30969 markerLength = dataView.getUint16(offset + 2) + 2;
30970 if (offset + markerLength > dataView.byteLength) {
30971 Roo.log('Invalid meta data: Invalid segment size.');
30975 if(markerBytes == 0xffe1){
30976 _this.parseExifData(
30983 offset += markerLength;
30993 var url = _this.urlAPI.createObjectURL(_this.file);
30995 _this.loadCanvas(url);
31000 reader.readAsArrayBuffer(this.file);
31006 parseExifData : function(dataView, offset, length)
31008 var tiffOffset = offset + 10,
31012 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31013 // No Exif data, might be XMP data instead
31017 // Check for the ASCII code for "Exif" (0x45786966):
31018 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31019 // No Exif data, might be XMP data instead
31022 if (tiffOffset + 8 > dataView.byteLength) {
31023 Roo.log('Invalid Exif data: Invalid segment size.');
31026 // Check for the two null bytes:
31027 if (dataView.getUint16(offset + 8) !== 0x0000) {
31028 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31031 // Check the byte alignment:
31032 switch (dataView.getUint16(tiffOffset)) {
31034 littleEndian = true;
31037 littleEndian = false;
31040 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31043 // Check for the TIFF tag marker (0x002A):
31044 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31045 Roo.log('Invalid Exif data: Missing TIFF marker.');
31048 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31049 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31051 this.parseExifTags(
31054 tiffOffset + dirOffset,
31059 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31064 if (dirOffset + 6 > dataView.byteLength) {
31065 Roo.log('Invalid Exif data: Invalid directory offset.');
31068 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31069 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31070 if (dirEndOffset + 4 > dataView.byteLength) {
31071 Roo.log('Invalid Exif data: Invalid directory size.');
31074 for (i = 0; i < tagsNumber; i += 1) {
31078 dirOffset + 2 + 12 * i, // tag offset
31082 // Return the offset to the next directory:
31083 return dataView.getUint32(dirEndOffset, littleEndian);
31086 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31088 var tag = dataView.getUint16(offset, littleEndian);
31090 this.exif[tag] = this.getExifValue(
31094 dataView.getUint16(offset + 2, littleEndian), // tag type
31095 dataView.getUint32(offset + 4, littleEndian), // tag length
31100 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31102 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31111 Roo.log('Invalid Exif data: Invalid tag type.');
31115 tagSize = tagType.size * length;
31116 // Determine if the value is contained in the dataOffset bytes,
31117 // or if the value at the dataOffset is a pointer to the actual data:
31118 dataOffset = tagSize > 4 ?
31119 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31120 if (dataOffset + tagSize > dataView.byteLength) {
31121 Roo.log('Invalid Exif data: Invalid data offset.');
31124 if (length === 1) {
31125 return tagType.getValue(dataView, dataOffset, littleEndian);
31128 for (i = 0; i < length; i += 1) {
31129 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31132 if (tagType.ascii) {
31134 // Concatenate the chars:
31135 for (i = 0; i < values.length; i += 1) {
31137 // Ignore the terminating NULL byte(s):
31138 if (c === '\u0000') {
31150 Roo.apply(Roo.bootstrap.UploadCropbox, {
31152 'Orientation': 0x0112
31156 1: 0, //'top-left',
31158 3: 180, //'bottom-right',
31159 // 4: 'bottom-left',
31161 6: 90, //'right-top',
31162 // 7: 'right-bottom',
31163 8: 270 //'left-bottom'
31167 // byte, 8-bit unsigned int:
31169 getValue: function (dataView, dataOffset) {
31170 return dataView.getUint8(dataOffset);
31174 // ascii, 8-bit byte:
31176 getValue: function (dataView, dataOffset) {
31177 return String.fromCharCode(dataView.getUint8(dataOffset));
31182 // short, 16 bit int:
31184 getValue: function (dataView, dataOffset, littleEndian) {
31185 return dataView.getUint16(dataOffset, littleEndian);
31189 // long, 32 bit int:
31191 getValue: function (dataView, dataOffset, littleEndian) {
31192 return dataView.getUint32(dataOffset, littleEndian);
31196 // rational = two long values, first is numerator, second is denominator:
31198 getValue: function (dataView, dataOffset, littleEndian) {
31199 return dataView.getUint32(dataOffset, littleEndian) /
31200 dataView.getUint32(dataOffset + 4, littleEndian);
31204 // slong, 32 bit signed int:
31206 getValue: function (dataView, dataOffset, littleEndian) {
31207 return dataView.getInt32(dataOffset, littleEndian);
31211 // srational, two slongs, first is numerator, second is denominator:
31213 getValue: function (dataView, dataOffset, littleEndian) {
31214 return dataView.getInt32(dataOffset, littleEndian) /
31215 dataView.getInt32(dataOffset + 4, littleEndian);
31225 cls : 'btn-group roo-upload-cropbox-rotate-left',
31226 action : 'rotate-left',
31230 cls : 'btn btn-default',
31231 html : '<i class="fa fa-undo"></i>'
31237 cls : 'btn-group roo-upload-cropbox-picture',
31238 action : 'picture',
31242 cls : 'btn btn-default',
31243 html : '<i class="fa fa-picture-o"></i>'
31249 cls : 'btn-group roo-upload-cropbox-rotate-right',
31250 action : 'rotate-right',
31254 cls : 'btn btn-default',
31255 html : '<i class="fa fa-repeat"></i>'
31263 cls : 'btn-group roo-upload-cropbox-rotate-left',
31264 action : 'rotate-left',
31268 cls : 'btn btn-default',
31269 html : '<i class="fa fa-undo"></i>'
31275 cls : 'btn-group roo-upload-cropbox-download',
31276 action : 'download',
31280 cls : 'btn btn-default',
31281 html : '<i class="fa fa-download"></i>'
31287 cls : 'btn-group roo-upload-cropbox-crop',
31292 cls : 'btn btn-default',
31293 html : '<i class="fa fa-crop"></i>'
31299 cls : 'btn-group roo-upload-cropbox-trash',
31304 cls : 'btn btn-default',
31305 html : '<i class="fa fa-trash"></i>'
31311 cls : 'btn-group roo-upload-cropbox-rotate-right',
31312 action : 'rotate-right',
31316 cls : 'btn btn-default',
31317 html : '<i class="fa fa-repeat"></i>'
31325 cls : 'btn-group roo-upload-cropbox-rotate-left',
31326 action : 'rotate-left',
31330 cls : 'btn btn-default',
31331 html : '<i class="fa fa-undo"></i>'
31337 cls : 'btn-group roo-upload-cropbox-rotate-right',
31338 action : 'rotate-right',
31342 cls : 'btn btn-default',
31343 html : '<i class="fa fa-repeat"></i>'
31356 * @class Roo.bootstrap.DocumentManager
31357 * @extends Roo.bootstrap.Component
31358 * Bootstrap DocumentManager class
31359 * @cfg {String} paramName default 'imageUpload'
31360 * @cfg {String} toolTipName default 'filename'
31361 * @cfg {String} method default POST
31362 * @cfg {String} url action url
31363 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31364 * @cfg {Boolean} multiple multiple upload default true
31365 * @cfg {Number} thumbSize default 300
31366 * @cfg {String} fieldLabel
31367 * @cfg {Number} labelWidth default 4
31368 * @cfg {String} labelAlign (left|top) default left
31369 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31370 * @cfg {Number} labellg set the width of label (1-12)
31371 * @cfg {Number} labelmd set the width of label (1-12)
31372 * @cfg {Number} labelsm set the width of label (1-12)
31373 * @cfg {Number} labelxs set the width of label (1-12)
31376 * Create a new DocumentManager
31377 * @param {Object} config The config object
31380 Roo.bootstrap.DocumentManager = function(config){
31381 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31384 this.delegates = [];
31389 * Fire when initial the DocumentManager
31390 * @param {Roo.bootstrap.DocumentManager} this
31395 * inspect selected file
31396 * @param {Roo.bootstrap.DocumentManager} this
31397 * @param {File} file
31402 * Fire when xhr load exception
31403 * @param {Roo.bootstrap.DocumentManager} this
31404 * @param {XMLHttpRequest} xhr
31406 "exception" : true,
31408 * @event afterupload
31409 * Fire when xhr load exception
31410 * @param {Roo.bootstrap.DocumentManager} this
31411 * @param {XMLHttpRequest} xhr
31413 "afterupload" : true,
31416 * prepare the form data
31417 * @param {Roo.bootstrap.DocumentManager} this
31418 * @param {Object} formData
31423 * Fire when remove the file
31424 * @param {Roo.bootstrap.DocumentManager} this
31425 * @param {Object} file
31430 * Fire after refresh the file
31431 * @param {Roo.bootstrap.DocumentManager} this
31436 * Fire after click the image
31437 * @param {Roo.bootstrap.DocumentManager} this
31438 * @param {Object} file
31443 * Fire when upload a image and editable set to true
31444 * @param {Roo.bootstrap.DocumentManager} this
31445 * @param {Object} file
31449 * @event beforeselectfile
31450 * Fire before select file
31451 * @param {Roo.bootstrap.DocumentManager} this
31453 "beforeselectfile" : true,
31456 * Fire before process file
31457 * @param {Roo.bootstrap.DocumentManager} this
31458 * @param {Object} file
31462 * @event previewrendered
31463 * Fire when preview rendered
31464 * @param {Roo.bootstrap.DocumentManager} this
31465 * @param {Object} file
31467 "previewrendered" : true,
31470 "previewResize" : true
31475 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31484 paramName : 'imageUpload',
31485 toolTipName : 'filename',
31488 labelAlign : 'left',
31498 getAutoCreate : function()
31500 var managerWidget = {
31502 cls : 'roo-document-manager',
31506 cls : 'roo-document-manager-selector',
31511 cls : 'roo-document-manager-uploader',
31515 cls : 'roo-document-manager-upload-btn',
31516 html : '<i class="fa fa-plus"></i>'
31527 cls : 'column col-md-12',
31532 if(this.fieldLabel.length){
31537 cls : 'column col-md-12',
31538 html : this.fieldLabel
31542 cls : 'column col-md-12',
31547 if(this.labelAlign == 'left'){
31552 html : this.fieldLabel
31561 if(this.labelWidth > 12){
31562 content[0].style = "width: " + this.labelWidth + 'px';
31565 if(this.labelWidth < 13 && this.labelmd == 0){
31566 this.labelmd = this.labelWidth;
31569 if(this.labellg > 0){
31570 content[0].cls += ' col-lg-' + this.labellg;
31571 content[1].cls += ' col-lg-' + (12 - this.labellg);
31574 if(this.labelmd > 0){
31575 content[0].cls += ' col-md-' + this.labelmd;
31576 content[1].cls += ' col-md-' + (12 - this.labelmd);
31579 if(this.labelsm > 0){
31580 content[0].cls += ' col-sm-' + this.labelsm;
31581 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31584 if(this.labelxs > 0){
31585 content[0].cls += ' col-xs-' + this.labelxs;
31586 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31594 cls : 'row clearfix',
31602 initEvents : function()
31604 this.managerEl = this.el.select('.roo-document-manager', true).first();
31605 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31607 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31608 this.selectorEl.hide();
31611 this.selectorEl.attr('multiple', 'multiple');
31614 this.selectorEl.on('change', this.onFileSelected, this);
31616 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31617 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31619 this.uploader.on('click', this.onUploaderClick, this);
31621 this.renderProgressDialog();
31625 window.addEventListener("resize", function() { _this.refresh(); } );
31627 this.fireEvent('initial', this);
31630 renderProgressDialog : function()
31634 this.progressDialog = new Roo.bootstrap.Modal({
31635 cls : 'roo-document-manager-progress-dialog',
31636 allow_close : false,
31647 btnclick : function() {
31648 _this.uploadCancel();
31654 this.progressDialog.render(Roo.get(document.body));
31656 this.progress = new Roo.bootstrap.Progress({
31657 cls : 'roo-document-manager-progress',
31662 this.progress.render(this.progressDialog.getChildContainer());
31664 this.progressBar = new Roo.bootstrap.ProgressBar({
31665 cls : 'roo-document-manager-progress-bar',
31668 aria_valuemax : 12,
31672 this.progressBar.render(this.progress.getChildContainer());
31675 onUploaderClick : function(e)
31677 e.preventDefault();
31679 if(this.fireEvent('beforeselectfile', this) != false){
31680 this.selectorEl.dom.click();
31685 onFileSelected : function(e)
31687 e.preventDefault();
31689 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31693 Roo.each(this.selectorEl.dom.files, function(file){
31694 if(this.fireEvent('inspect', this, file) != false){
31695 this.files.push(file);
31705 this.selectorEl.dom.value = '';
31707 if(!this.files || !this.files.length){
31711 if(this.boxes > 0 && this.files.length > this.boxes){
31712 this.files = this.files.slice(0, this.boxes);
31715 this.uploader.show();
31717 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31718 this.uploader.hide();
31727 Roo.each(this.files, function(file){
31729 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31730 var f = this.renderPreview(file);
31735 if(file.type.indexOf('image') != -1){
31736 this.delegates.push(
31738 _this.process(file);
31739 }).createDelegate(this)
31747 _this.process(file);
31748 }).createDelegate(this)
31753 this.files = files;
31755 this.delegates = this.delegates.concat(docs);
31757 if(!this.delegates.length){
31762 this.progressBar.aria_valuemax = this.delegates.length;
31769 arrange : function()
31771 if(!this.delegates.length){
31772 this.progressDialog.hide();
31777 var delegate = this.delegates.shift();
31779 this.progressDialog.show();
31781 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31783 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31788 refresh : function()
31790 this.uploader.show();
31792 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31793 this.uploader.hide();
31796 Roo.isTouch ? this.closable(false) : this.closable(true);
31798 this.fireEvent('refresh', this);
31801 onRemove : function(e, el, o)
31803 e.preventDefault();
31805 this.fireEvent('remove', this, o);
31809 remove : function(o)
31813 Roo.each(this.files, function(file){
31814 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31823 this.files = files;
31830 Roo.each(this.files, function(file){
31835 file.target.remove();
31844 onClick : function(e, el, o)
31846 e.preventDefault();
31848 this.fireEvent('click', this, o);
31852 closable : function(closable)
31854 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31856 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31868 xhrOnLoad : function(xhr)
31870 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31874 if (xhr.readyState !== 4) {
31876 this.fireEvent('exception', this, xhr);
31880 var response = Roo.decode(xhr.responseText);
31882 if(!response.success){
31884 this.fireEvent('exception', this, xhr);
31888 var file = this.renderPreview(response.data);
31890 this.files.push(file);
31894 this.fireEvent('afterupload', this, xhr);
31898 xhrOnError : function(xhr)
31900 Roo.log('xhr on error');
31902 var response = Roo.decode(xhr.responseText);
31909 process : function(file)
31911 if(this.fireEvent('process', this, file) !== false){
31912 if(this.editable && file.type.indexOf('image') != -1){
31913 this.fireEvent('edit', this, file);
31917 this.uploadStart(file, false);
31924 uploadStart : function(file, crop)
31926 this.xhr = new XMLHttpRequest();
31928 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31933 file.xhr = this.xhr;
31935 this.managerEl.createChild({
31937 cls : 'roo-document-manager-loading',
31941 tooltip : file.name,
31942 cls : 'roo-document-manager-thumb',
31943 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31949 this.xhr.open(this.method, this.url, true);
31952 "Accept": "application/json",
31953 "Cache-Control": "no-cache",
31954 "X-Requested-With": "XMLHttpRequest"
31957 for (var headerName in headers) {
31958 var headerValue = headers[headerName];
31960 this.xhr.setRequestHeader(headerName, headerValue);
31966 this.xhr.onload = function()
31968 _this.xhrOnLoad(_this.xhr);
31971 this.xhr.onerror = function()
31973 _this.xhrOnError(_this.xhr);
31976 var formData = new FormData();
31978 formData.append('returnHTML', 'NO');
31981 formData.append('crop', crop);
31984 formData.append(this.paramName, file, file.name);
31991 if(this.fireEvent('prepare', this, formData, options) != false){
31993 if(options.manually){
31997 this.xhr.send(formData);
32001 this.uploadCancel();
32004 uploadCancel : function()
32010 this.delegates = [];
32012 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32019 renderPreview : function(file)
32021 if(typeof(file.target) != 'undefined' && file.target){
32025 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32027 var previewEl = this.managerEl.createChild({
32029 cls : 'roo-document-manager-preview',
32033 tooltip : file[this.toolTipName],
32034 cls : 'roo-document-manager-thumb',
32035 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32040 html : '<i class="fa fa-times-circle"></i>'
32045 var close = previewEl.select('button.close', true).first();
32047 close.on('click', this.onRemove, this, file);
32049 file.target = previewEl;
32051 var image = previewEl.select('img', true).first();
32055 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32057 image.on('click', this.onClick, this, file);
32059 this.fireEvent('previewrendered', this, file);
32065 onPreviewLoad : function(file, image)
32067 if(typeof(file.target) == 'undefined' || !file.target){
32071 var width = image.dom.naturalWidth || image.dom.width;
32072 var height = image.dom.naturalHeight || image.dom.height;
32074 if(!this.previewResize) {
32078 if(width > height){
32079 file.target.addClass('wide');
32083 file.target.addClass('tall');
32088 uploadFromSource : function(file, crop)
32090 this.xhr = new XMLHttpRequest();
32092 this.managerEl.createChild({
32094 cls : 'roo-document-manager-loading',
32098 tooltip : file.name,
32099 cls : 'roo-document-manager-thumb',
32100 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32106 this.xhr.open(this.method, this.url, true);
32109 "Accept": "application/json",
32110 "Cache-Control": "no-cache",
32111 "X-Requested-With": "XMLHttpRequest"
32114 for (var headerName in headers) {
32115 var headerValue = headers[headerName];
32117 this.xhr.setRequestHeader(headerName, headerValue);
32123 this.xhr.onload = function()
32125 _this.xhrOnLoad(_this.xhr);
32128 this.xhr.onerror = function()
32130 _this.xhrOnError(_this.xhr);
32133 var formData = new FormData();
32135 formData.append('returnHTML', 'NO');
32137 formData.append('crop', crop);
32139 if(typeof(file.filename) != 'undefined'){
32140 formData.append('filename', file.filename);
32143 if(typeof(file.mimetype) != 'undefined'){
32144 formData.append('mimetype', file.mimetype);
32149 if(this.fireEvent('prepare', this, formData) != false){
32150 this.xhr.send(formData);
32160 * @class Roo.bootstrap.DocumentViewer
32161 * @extends Roo.bootstrap.Component
32162 * Bootstrap DocumentViewer class
32163 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32164 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32167 * Create a new DocumentViewer
32168 * @param {Object} config The config object
32171 Roo.bootstrap.DocumentViewer = function(config){
32172 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32177 * Fire after initEvent
32178 * @param {Roo.bootstrap.DocumentViewer} this
32184 * @param {Roo.bootstrap.DocumentViewer} this
32189 * Fire after download button
32190 * @param {Roo.bootstrap.DocumentViewer} this
32195 * Fire after trash button
32196 * @param {Roo.bootstrap.DocumentViewer} this
32203 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32205 showDownload : true,
32209 getAutoCreate : function()
32213 cls : 'roo-document-viewer',
32217 cls : 'roo-document-viewer-body',
32221 cls : 'roo-document-viewer-thumb',
32225 cls : 'roo-document-viewer-image'
32233 cls : 'roo-document-viewer-footer',
32236 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32240 cls : 'btn-group roo-document-viewer-download',
32244 cls : 'btn btn-default',
32245 html : '<i class="fa fa-download"></i>'
32251 cls : 'btn-group roo-document-viewer-trash',
32255 cls : 'btn btn-default',
32256 html : '<i class="fa fa-trash"></i>'
32269 initEvents : function()
32271 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32272 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32274 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32275 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32277 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32278 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32280 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32281 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32283 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32284 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32286 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32287 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32289 this.bodyEl.on('click', this.onClick, this);
32290 this.downloadBtn.on('click', this.onDownload, this);
32291 this.trashBtn.on('click', this.onTrash, this);
32293 this.downloadBtn.hide();
32294 this.trashBtn.hide();
32296 if(this.showDownload){
32297 this.downloadBtn.show();
32300 if(this.showTrash){
32301 this.trashBtn.show();
32304 if(!this.showDownload && !this.showTrash) {
32305 this.footerEl.hide();
32310 initial : function()
32312 this.fireEvent('initial', this);
32316 onClick : function(e)
32318 e.preventDefault();
32320 this.fireEvent('click', this);
32323 onDownload : function(e)
32325 e.preventDefault();
32327 this.fireEvent('download', this);
32330 onTrash : function(e)
32332 e.preventDefault();
32334 this.fireEvent('trash', this);
32346 * @class Roo.bootstrap.NavProgressBar
32347 * @extends Roo.bootstrap.Component
32348 * Bootstrap NavProgressBar class
32351 * Create a new nav progress bar
32352 * @param {Object} config The config object
32355 Roo.bootstrap.NavProgressBar = function(config){
32356 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32358 this.bullets = this.bullets || [];
32360 // Roo.bootstrap.NavProgressBar.register(this);
32364 * Fires when the active item changes
32365 * @param {Roo.bootstrap.NavProgressBar} this
32366 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32367 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32374 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32379 getAutoCreate : function()
32381 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32385 cls : 'roo-navigation-bar-group',
32389 cls : 'roo-navigation-top-bar'
32393 cls : 'roo-navigation-bullets-bar',
32397 cls : 'roo-navigation-bar'
32404 cls : 'roo-navigation-bottom-bar'
32414 initEvents: function()
32419 onRender : function(ct, position)
32421 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32423 if(this.bullets.length){
32424 Roo.each(this.bullets, function(b){
32433 addItem : function(cfg)
32435 var item = new Roo.bootstrap.NavProgressItem(cfg);
32437 item.parentId = this.id;
32438 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32441 var top = new Roo.bootstrap.Element({
32443 cls : 'roo-navigation-bar-text'
32446 var bottom = new Roo.bootstrap.Element({
32448 cls : 'roo-navigation-bar-text'
32451 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32452 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32454 var topText = new Roo.bootstrap.Element({
32456 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32459 var bottomText = new Roo.bootstrap.Element({
32461 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32464 topText.onRender(top.el, null);
32465 bottomText.onRender(bottom.el, null);
32468 item.bottomEl = bottom;
32471 this.barItems.push(item);
32476 getActive : function()
32478 var active = false;
32480 Roo.each(this.barItems, function(v){
32482 if (!v.isActive()) {
32494 setActiveItem : function(item)
32498 Roo.each(this.barItems, function(v){
32499 if (v.rid == item.rid) {
32503 if (v.isActive()) {
32504 v.setActive(false);
32509 item.setActive(true);
32511 this.fireEvent('changed', this, item, prev);
32514 getBarItem: function(rid)
32518 Roo.each(this.barItems, function(e) {
32519 if (e.rid != rid) {
32530 indexOfItem : function(item)
32534 Roo.each(this.barItems, function(v, i){
32536 if (v.rid != item.rid) {
32547 setActiveNext : function()
32549 var i = this.indexOfItem(this.getActive());
32551 if (i > this.barItems.length) {
32555 this.setActiveItem(this.barItems[i+1]);
32558 setActivePrev : function()
32560 var i = this.indexOfItem(this.getActive());
32566 this.setActiveItem(this.barItems[i-1]);
32569 format : function()
32571 if(!this.barItems.length){
32575 var width = 100 / this.barItems.length;
32577 Roo.each(this.barItems, function(i){
32578 i.el.setStyle('width', width + '%');
32579 i.topEl.el.setStyle('width', width + '%');
32580 i.bottomEl.el.setStyle('width', width + '%');
32589 * Nav Progress Item
32594 * @class Roo.bootstrap.NavProgressItem
32595 * @extends Roo.bootstrap.Component
32596 * Bootstrap NavProgressItem class
32597 * @cfg {String} rid the reference id
32598 * @cfg {Boolean} active (true|false) Is item active default false
32599 * @cfg {Boolean} disabled (true|false) Is item active default false
32600 * @cfg {String} html
32601 * @cfg {String} position (top|bottom) text position default bottom
32602 * @cfg {String} icon show icon instead of number
32605 * Create a new NavProgressItem
32606 * @param {Object} config The config object
32608 Roo.bootstrap.NavProgressItem = function(config){
32609 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32614 * The raw click event for the entire grid.
32615 * @param {Roo.bootstrap.NavProgressItem} this
32616 * @param {Roo.EventObject} e
32623 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32629 position : 'bottom',
32632 getAutoCreate : function()
32634 var iconCls = 'roo-navigation-bar-item-icon';
32636 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32640 cls: 'roo-navigation-bar-item',
32650 cfg.cls += ' active';
32653 cfg.cls += ' disabled';
32659 disable : function()
32661 this.setDisabled(true);
32664 enable : function()
32666 this.setDisabled(false);
32669 initEvents: function()
32671 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32673 this.iconEl.on('click', this.onClick, this);
32676 onClick : function(e)
32678 e.preventDefault();
32684 if(this.fireEvent('click', this, e) === false){
32688 this.parent().setActiveItem(this);
32691 isActive: function ()
32693 return this.active;
32696 setActive : function(state)
32698 if(this.active == state){
32702 this.active = state;
32705 this.el.addClass('active');
32709 this.el.removeClass('active');
32714 setDisabled : function(state)
32716 if(this.disabled == state){
32720 this.disabled = state;
32723 this.el.addClass('disabled');
32727 this.el.removeClass('disabled');
32730 tooltipEl : function()
32732 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32745 * @class Roo.bootstrap.FieldLabel
32746 * @extends Roo.bootstrap.Component
32747 * Bootstrap FieldLabel class
32748 * @cfg {String} html contents of the element
32749 * @cfg {String} tag tag of the element default label
32750 * @cfg {String} cls class of the element
32751 * @cfg {String} target label target
32752 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32753 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32754 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32755 * @cfg {String} iconTooltip default "This field is required"
32756 * @cfg {String} indicatorpos (left|right) default left
32759 * Create a new FieldLabel
32760 * @param {Object} config The config object
32763 Roo.bootstrap.FieldLabel = function(config){
32764 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32769 * Fires after the field has been marked as invalid.
32770 * @param {Roo.form.FieldLabel} this
32771 * @param {String} msg The validation message
32776 * Fires after the field has been validated with no errors.
32777 * @param {Roo.form.FieldLabel} this
32783 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32790 invalidClass : 'has-warning',
32791 validClass : 'has-success',
32792 iconTooltip : 'This field is required',
32793 indicatorpos : 'left',
32795 getAutoCreate : function(){
32798 if (!this.allowBlank) {
32804 cls : 'roo-bootstrap-field-label ' + this.cls,
32809 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32810 tooltip : this.iconTooltip
32819 if(this.indicatorpos == 'right'){
32822 cls : 'roo-bootstrap-field-label ' + this.cls,
32831 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32832 tooltip : this.iconTooltip
32841 initEvents: function()
32843 Roo.bootstrap.Element.superclass.initEvents.call(this);
32845 this.indicator = this.indicatorEl();
32847 if(this.indicator){
32848 this.indicator.removeClass('visible');
32849 this.indicator.addClass('invisible');
32852 Roo.bootstrap.FieldLabel.register(this);
32855 indicatorEl : function()
32857 var indicator = this.el.select('i.roo-required-indicator',true).first();
32868 * Mark this field as valid
32870 markValid : function()
32872 if(this.indicator){
32873 this.indicator.removeClass('visible');
32874 this.indicator.addClass('invisible');
32876 if (Roo.bootstrap.version == 3) {
32877 this.el.removeClass(this.invalidClass);
32878 this.el.addClass(this.validClass);
32880 this.el.removeClass('is-invalid');
32881 this.el.addClass('is-valid');
32885 this.fireEvent('valid', this);
32889 * Mark this field as invalid
32890 * @param {String} msg The validation message
32892 markInvalid : function(msg)
32894 if(this.indicator){
32895 this.indicator.removeClass('invisible');
32896 this.indicator.addClass('visible');
32898 if (Roo.bootstrap.version == 3) {
32899 this.el.removeClass(this.validClass);
32900 this.el.addClass(this.invalidClass);
32902 this.el.removeClass('is-valid');
32903 this.el.addClass('is-invalid');
32907 this.fireEvent('invalid', this, msg);
32913 Roo.apply(Roo.bootstrap.FieldLabel, {
32918 * register a FieldLabel Group
32919 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32921 register : function(label)
32923 if(this.groups.hasOwnProperty(label.target)){
32927 this.groups[label.target] = label;
32931 * fetch a FieldLabel Group based on the target
32932 * @param {string} target
32933 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32935 get: function(target) {
32936 if (typeof(this.groups[target]) == 'undefined') {
32940 return this.groups[target] ;
32949 * page DateSplitField.
32955 * @class Roo.bootstrap.DateSplitField
32956 * @extends Roo.bootstrap.Component
32957 * Bootstrap DateSplitField class
32958 * @cfg {string} fieldLabel - the label associated
32959 * @cfg {Number} labelWidth set the width of label (0-12)
32960 * @cfg {String} labelAlign (top|left)
32961 * @cfg {Boolean} dayAllowBlank (true|false) default false
32962 * @cfg {Boolean} monthAllowBlank (true|false) default false
32963 * @cfg {Boolean} yearAllowBlank (true|false) default false
32964 * @cfg {string} dayPlaceholder
32965 * @cfg {string} monthPlaceholder
32966 * @cfg {string} yearPlaceholder
32967 * @cfg {string} dayFormat default 'd'
32968 * @cfg {string} monthFormat default 'm'
32969 * @cfg {string} yearFormat default 'Y'
32970 * @cfg {Number} labellg set the width of label (1-12)
32971 * @cfg {Number} labelmd set the width of label (1-12)
32972 * @cfg {Number} labelsm set the width of label (1-12)
32973 * @cfg {Number} labelxs set the width of label (1-12)
32977 * Create a new DateSplitField
32978 * @param {Object} config The config object
32981 Roo.bootstrap.DateSplitField = function(config){
32982 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32988 * getting the data of years
32989 * @param {Roo.bootstrap.DateSplitField} this
32990 * @param {Object} years
32995 * getting the data of days
32996 * @param {Roo.bootstrap.DateSplitField} this
32997 * @param {Object} days
33002 * Fires after the field has been marked as invalid.
33003 * @param {Roo.form.Field} this
33004 * @param {String} msg The validation message
33009 * Fires after the field has been validated with no errors.
33010 * @param {Roo.form.Field} this
33016 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33019 labelAlign : 'top',
33021 dayAllowBlank : false,
33022 monthAllowBlank : false,
33023 yearAllowBlank : false,
33024 dayPlaceholder : '',
33025 monthPlaceholder : '',
33026 yearPlaceholder : '',
33030 isFormField : true,
33036 getAutoCreate : function()
33040 cls : 'row roo-date-split-field-group',
33045 cls : 'form-hidden-field roo-date-split-field-group-value',
33051 var labelCls = 'col-md-12';
33052 var contentCls = 'col-md-4';
33054 if(this.fieldLabel){
33058 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33062 html : this.fieldLabel
33067 if(this.labelAlign == 'left'){
33069 if(this.labelWidth > 12){
33070 label.style = "width: " + this.labelWidth + 'px';
33073 if(this.labelWidth < 13 && this.labelmd == 0){
33074 this.labelmd = this.labelWidth;
33077 if(this.labellg > 0){
33078 labelCls = ' col-lg-' + this.labellg;
33079 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33082 if(this.labelmd > 0){
33083 labelCls = ' col-md-' + this.labelmd;
33084 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33087 if(this.labelsm > 0){
33088 labelCls = ' col-sm-' + this.labelsm;
33089 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33092 if(this.labelxs > 0){
33093 labelCls = ' col-xs-' + this.labelxs;
33094 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33098 label.cls += ' ' + labelCls;
33100 cfg.cn.push(label);
33103 Roo.each(['day', 'month', 'year'], function(t){
33106 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33113 inputEl: function ()
33115 return this.el.select('.roo-date-split-field-group-value', true).first();
33118 onRender : function(ct, position)
33122 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33124 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33126 this.dayField = new Roo.bootstrap.ComboBox({
33127 allowBlank : this.dayAllowBlank,
33128 alwaysQuery : true,
33129 displayField : 'value',
33132 forceSelection : true,
33134 placeholder : this.dayPlaceholder,
33135 selectOnFocus : true,
33136 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33137 triggerAction : 'all',
33139 valueField : 'value',
33140 store : new Roo.data.SimpleStore({
33141 data : (function() {
33143 _this.fireEvent('days', _this, days);
33146 fields : [ 'value' ]
33149 select : function (_self, record, index)
33151 _this.setValue(_this.getValue());
33156 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33158 this.monthField = new Roo.bootstrap.MonthField({
33159 after : '<i class=\"fa fa-calendar\"></i>',
33160 allowBlank : this.monthAllowBlank,
33161 placeholder : this.monthPlaceholder,
33164 render : function (_self)
33166 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33167 e.preventDefault();
33171 select : function (_self, oldvalue, newvalue)
33173 _this.setValue(_this.getValue());
33178 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33180 this.yearField = new Roo.bootstrap.ComboBox({
33181 allowBlank : this.yearAllowBlank,
33182 alwaysQuery : true,
33183 displayField : 'value',
33186 forceSelection : true,
33188 placeholder : this.yearPlaceholder,
33189 selectOnFocus : true,
33190 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33191 triggerAction : 'all',
33193 valueField : 'value',
33194 store : new Roo.data.SimpleStore({
33195 data : (function() {
33197 _this.fireEvent('years', _this, years);
33200 fields : [ 'value' ]
33203 select : function (_self, record, index)
33205 _this.setValue(_this.getValue());
33210 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33213 setValue : function(v, format)
33215 this.inputEl.dom.value = v;
33217 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33219 var d = Date.parseDate(v, f);
33226 this.setDay(d.format(this.dayFormat));
33227 this.setMonth(d.format(this.monthFormat));
33228 this.setYear(d.format(this.yearFormat));
33235 setDay : function(v)
33237 this.dayField.setValue(v);
33238 this.inputEl.dom.value = this.getValue();
33243 setMonth : function(v)
33245 this.monthField.setValue(v, true);
33246 this.inputEl.dom.value = this.getValue();
33251 setYear : function(v)
33253 this.yearField.setValue(v);
33254 this.inputEl.dom.value = this.getValue();
33259 getDay : function()
33261 return this.dayField.getValue();
33264 getMonth : function()
33266 return this.monthField.getValue();
33269 getYear : function()
33271 return this.yearField.getValue();
33274 getValue : function()
33276 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33278 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33288 this.inputEl.dom.value = '';
33293 validate : function()
33295 var d = this.dayField.validate();
33296 var m = this.monthField.validate();
33297 var y = this.yearField.validate();
33302 (!this.dayAllowBlank && !d) ||
33303 (!this.monthAllowBlank && !m) ||
33304 (!this.yearAllowBlank && !y)
33309 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33318 this.markInvalid();
33323 markValid : function()
33326 var label = this.el.select('label', true).first();
33327 var icon = this.el.select('i.fa-star', true).first();
33333 this.fireEvent('valid', this);
33337 * Mark this field as invalid
33338 * @param {String} msg The validation message
33340 markInvalid : function(msg)
33343 var label = this.el.select('label', true).first();
33344 var icon = this.el.select('i.fa-star', true).first();
33346 if(label && !icon){
33347 this.el.select('.roo-date-split-field-label', true).createChild({
33349 cls : 'text-danger fa fa-lg fa-star',
33350 tooltip : 'This field is required',
33351 style : 'margin-right:5px;'
33355 this.fireEvent('invalid', this, msg);
33358 clearInvalid : function()
33360 var label = this.el.select('label', true).first();
33361 var icon = this.el.select('i.fa-star', true).first();
33367 this.fireEvent('valid', this);
33370 getName: function()
33380 * http://masonry.desandro.com
33382 * The idea is to render all the bricks based on vertical width...
33384 * The original code extends 'outlayer' - we might need to use that....
33390 * @class Roo.bootstrap.LayoutMasonry
33391 * @extends Roo.bootstrap.Component
33392 * Bootstrap Layout Masonry class
33395 * Create a new Element
33396 * @param {Object} config The config object
33399 Roo.bootstrap.LayoutMasonry = function(config){
33401 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33405 Roo.bootstrap.LayoutMasonry.register(this);
33411 * Fire after layout the items
33412 * @param {Roo.bootstrap.LayoutMasonry} this
33413 * @param {Roo.EventObject} e
33420 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33423 * @cfg {Boolean} isLayoutInstant = no animation?
33425 isLayoutInstant : false, // needed?
33428 * @cfg {Number} boxWidth width of the columns
33433 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33438 * @cfg {Number} padWidth padding below box..
33443 * @cfg {Number} gutter gutter width..
33448 * @cfg {Number} maxCols maximum number of columns
33454 * @cfg {Boolean} isAutoInitial defalut true
33456 isAutoInitial : true,
33461 * @cfg {Boolean} isHorizontal defalut false
33463 isHorizontal : false,
33465 currentSize : null,
33471 bricks: null, //CompositeElement
33475 _isLayoutInited : false,
33477 // isAlternative : false, // only use for vertical layout...
33480 * @cfg {Number} alternativePadWidth padding below box..
33482 alternativePadWidth : 50,
33484 selectedBrick : [],
33486 getAutoCreate : function(){
33488 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33492 cls: 'blog-masonary-wrapper ' + this.cls,
33494 cls : 'mas-boxes masonary'
33501 getChildContainer: function( )
33503 if (this.boxesEl) {
33504 return this.boxesEl;
33507 this.boxesEl = this.el.select('.mas-boxes').first();
33509 return this.boxesEl;
33513 initEvents : function()
33517 if(this.isAutoInitial){
33518 Roo.log('hook children rendered');
33519 this.on('childrenrendered', function() {
33520 Roo.log('children rendered');
33526 initial : function()
33528 this.selectedBrick = [];
33530 this.currentSize = this.el.getBox(true);
33532 Roo.EventManager.onWindowResize(this.resize, this);
33534 if(!this.isAutoInitial){
33542 //this.layout.defer(500,this);
33546 resize : function()
33548 var cs = this.el.getBox(true);
33551 this.currentSize.width == cs.width &&
33552 this.currentSize.x == cs.x &&
33553 this.currentSize.height == cs.height &&
33554 this.currentSize.y == cs.y
33556 Roo.log("no change in with or X or Y");
33560 this.currentSize = cs;
33566 layout : function()
33568 this._resetLayout();
33570 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33572 this.layoutItems( isInstant );
33574 this._isLayoutInited = true;
33576 this.fireEvent('layout', this);
33580 _resetLayout : function()
33582 if(this.isHorizontal){
33583 this.horizontalMeasureColumns();
33587 this.verticalMeasureColumns();
33591 verticalMeasureColumns : function()
33593 this.getContainerWidth();
33595 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33596 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33600 var boxWidth = this.boxWidth + this.padWidth;
33602 if(this.containerWidth < this.boxWidth){
33603 boxWidth = this.containerWidth
33606 var containerWidth = this.containerWidth;
33608 var cols = Math.floor(containerWidth / boxWidth);
33610 this.cols = Math.max( cols, 1 );
33612 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33614 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33616 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33618 this.colWidth = boxWidth + avail - this.padWidth;
33620 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33621 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33624 horizontalMeasureColumns : function()
33626 this.getContainerWidth();
33628 var boxWidth = this.boxWidth;
33630 if(this.containerWidth < boxWidth){
33631 boxWidth = this.containerWidth;
33634 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33636 this.el.setHeight(boxWidth);
33640 getContainerWidth : function()
33642 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33645 layoutItems : function( isInstant )
33647 Roo.log(this.bricks);
33649 var items = Roo.apply([], this.bricks);
33651 if(this.isHorizontal){
33652 this._horizontalLayoutItems( items , isInstant );
33656 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33657 // this._verticalAlternativeLayoutItems( items , isInstant );
33661 this._verticalLayoutItems( items , isInstant );
33665 _verticalLayoutItems : function ( items , isInstant)
33667 if ( !items || !items.length ) {
33672 ['xs', 'xs', 'xs', 'tall'],
33673 ['xs', 'xs', 'tall'],
33674 ['xs', 'xs', 'sm'],
33675 ['xs', 'xs', 'xs'],
33681 ['sm', 'xs', 'xs'],
33685 ['tall', 'xs', 'xs', 'xs'],
33686 ['tall', 'xs', 'xs'],
33698 Roo.each(items, function(item, k){
33700 switch (item.size) {
33701 // these layouts take up a full box,
33712 boxes.push([item]);
33735 var filterPattern = function(box, length)
33743 var pattern = box.slice(0, length);
33747 Roo.each(pattern, function(i){
33748 format.push(i.size);
33751 Roo.each(standard, function(s){
33753 if(String(s) != String(format)){
33762 if(!match && length == 1){
33767 filterPattern(box, length - 1);
33771 queue.push(pattern);
33773 box = box.slice(length, box.length);
33775 filterPattern(box, 4);
33781 Roo.each(boxes, function(box, k){
33787 if(box.length == 1){
33792 filterPattern(box, 4);
33796 this._processVerticalLayoutQueue( queue, isInstant );
33800 // _verticalAlternativeLayoutItems : function( items , isInstant )
33802 // if ( !items || !items.length ) {
33806 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33810 _horizontalLayoutItems : function ( items , isInstant)
33812 if ( !items || !items.length || items.length < 3) {
33818 var eItems = items.slice(0, 3);
33820 items = items.slice(3, items.length);
33823 ['xs', 'xs', 'xs', 'wide'],
33824 ['xs', 'xs', 'wide'],
33825 ['xs', 'xs', 'sm'],
33826 ['xs', 'xs', 'xs'],
33832 ['sm', 'xs', 'xs'],
33836 ['wide', 'xs', 'xs', 'xs'],
33837 ['wide', 'xs', 'xs'],
33850 Roo.each(items, function(item, k){
33852 switch (item.size) {
33863 boxes.push([item]);
33887 var filterPattern = function(box, length)
33895 var pattern = box.slice(0, length);
33899 Roo.each(pattern, function(i){
33900 format.push(i.size);
33903 Roo.each(standard, function(s){
33905 if(String(s) != String(format)){
33914 if(!match && length == 1){
33919 filterPattern(box, length - 1);
33923 queue.push(pattern);
33925 box = box.slice(length, box.length);
33927 filterPattern(box, 4);
33933 Roo.each(boxes, function(box, k){
33939 if(box.length == 1){
33944 filterPattern(box, 4);
33951 var pos = this.el.getBox(true);
33955 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33957 var hit_end = false;
33959 Roo.each(queue, function(box){
33963 Roo.each(box, function(b){
33965 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33975 Roo.each(box, function(b){
33977 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33980 mx = Math.max(mx, b.x);
33984 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33988 Roo.each(box, function(b){
33990 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34004 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34007 /** Sets position of item in DOM
34008 * @param {Element} item
34009 * @param {Number} x - horizontal position
34010 * @param {Number} y - vertical position
34011 * @param {Boolean} isInstant - disables transitions
34013 _processVerticalLayoutQueue : function( queue, isInstant )
34015 var pos = this.el.getBox(true);
34020 for (var i = 0; i < this.cols; i++){
34024 Roo.each(queue, function(box, k){
34026 var col = k % this.cols;
34028 Roo.each(box, function(b,kk){
34030 b.el.position('absolute');
34032 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34033 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34035 if(b.size == 'md-left' || b.size == 'md-right'){
34036 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34037 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34040 b.el.setWidth(width);
34041 b.el.setHeight(height);
34043 b.el.select('iframe',true).setSize(width,height);
34047 for (var i = 0; i < this.cols; i++){
34049 if(maxY[i] < maxY[col]){
34054 col = Math.min(col, i);
34058 x = pos.x + col * (this.colWidth + this.padWidth);
34062 var positions = [];
34064 switch (box.length){
34066 positions = this.getVerticalOneBoxColPositions(x, y, box);
34069 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34072 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34075 positions = this.getVerticalFourBoxColPositions(x, y, box);
34081 Roo.each(box, function(b,kk){
34083 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34085 var sz = b.el.getSize();
34087 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34095 for (var i = 0; i < this.cols; i++){
34096 mY = Math.max(mY, maxY[i]);
34099 this.el.setHeight(mY - pos.y);
34103 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34105 // var pos = this.el.getBox(true);
34108 // var maxX = pos.right;
34110 // var maxHeight = 0;
34112 // Roo.each(items, function(item, k){
34116 // item.el.position('absolute');
34118 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34120 // item.el.setWidth(width);
34122 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34124 // item.el.setHeight(height);
34127 // item.el.setXY([x, y], isInstant ? false : true);
34129 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34132 // y = y + height + this.alternativePadWidth;
34134 // maxHeight = maxHeight + height + this.alternativePadWidth;
34138 // this.el.setHeight(maxHeight);
34142 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34144 var pos = this.el.getBox(true);
34149 var maxX = pos.right;
34151 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34153 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34155 Roo.each(queue, function(box, k){
34157 Roo.each(box, function(b, kk){
34159 b.el.position('absolute');
34161 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34162 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34164 if(b.size == 'md-left' || b.size == 'md-right'){
34165 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34166 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34169 b.el.setWidth(width);
34170 b.el.setHeight(height);
34178 var positions = [];
34180 switch (box.length){
34182 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34185 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34188 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34191 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34197 Roo.each(box, function(b,kk){
34199 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34201 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34209 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34211 Roo.each(eItems, function(b,k){
34213 b.size = (k == 0) ? 'sm' : 'xs';
34214 b.x = (k == 0) ? 2 : 1;
34215 b.y = (k == 0) ? 2 : 1;
34217 b.el.position('absolute');
34219 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34221 b.el.setWidth(width);
34223 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34225 b.el.setHeight(height);
34229 var positions = [];
34232 x : maxX - this.unitWidth * 2 - this.gutter,
34237 x : maxX - this.unitWidth,
34238 y : minY + (this.unitWidth + this.gutter) * 2
34242 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34246 Roo.each(eItems, function(b,k){
34248 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34254 getVerticalOneBoxColPositions : function(x, y, box)
34258 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34260 if(box[0].size == 'md-left'){
34264 if(box[0].size == 'md-right'){
34269 x : x + (this.unitWidth + this.gutter) * rand,
34276 getVerticalTwoBoxColPositions : function(x, y, box)
34280 if(box[0].size == 'xs'){
34284 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34288 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34302 x : x + (this.unitWidth + this.gutter) * 2,
34303 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34310 getVerticalThreeBoxColPositions : function(x, y, box)
34314 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34322 x : x + (this.unitWidth + this.gutter) * 1,
34327 x : x + (this.unitWidth + this.gutter) * 2,
34335 if(box[0].size == 'xs' && box[1].size == 'xs'){
34344 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34348 x : x + (this.unitWidth + this.gutter) * 1,
34362 x : x + (this.unitWidth + this.gutter) * 2,
34367 x : x + (this.unitWidth + this.gutter) * 2,
34368 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34375 getVerticalFourBoxColPositions : function(x, y, box)
34379 if(box[0].size == 'xs'){
34388 y : y + (this.unitHeight + this.gutter) * 1
34393 y : y + (this.unitHeight + this.gutter) * 2
34397 x : x + (this.unitWidth + this.gutter) * 1,
34411 x : x + (this.unitWidth + this.gutter) * 2,
34416 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34417 y : y + (this.unitHeight + this.gutter) * 1
34421 x : x + (this.unitWidth + this.gutter) * 2,
34422 y : y + (this.unitWidth + this.gutter) * 2
34429 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34433 if(box[0].size == 'md-left'){
34435 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34442 if(box[0].size == 'md-right'){
34444 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34445 y : minY + (this.unitWidth + this.gutter) * 1
34451 var rand = Math.floor(Math.random() * (4 - box[0].y));
34454 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34455 y : minY + (this.unitWidth + this.gutter) * rand
34462 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34466 if(box[0].size == 'xs'){
34469 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34474 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34475 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34483 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34488 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34489 y : minY + (this.unitWidth + this.gutter) * 2
34496 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34500 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34503 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34508 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34509 y : minY + (this.unitWidth + this.gutter) * 1
34513 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34514 y : minY + (this.unitWidth + this.gutter) * 2
34521 if(box[0].size == 'xs' && box[1].size == 'xs'){
34524 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34529 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34534 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34535 y : minY + (this.unitWidth + this.gutter) * 1
34543 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34548 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34549 y : minY + (this.unitWidth + this.gutter) * 2
34553 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34554 y : minY + (this.unitWidth + this.gutter) * 2
34561 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34565 if(box[0].size == 'xs'){
34568 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34573 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34578 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34583 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34584 y : minY + (this.unitWidth + this.gutter) * 1
34592 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34597 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34598 y : minY + (this.unitWidth + this.gutter) * 2
34602 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34603 y : minY + (this.unitWidth + this.gutter) * 2
34607 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34608 y : minY + (this.unitWidth + this.gutter) * 2
34616 * remove a Masonry Brick
34617 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34619 removeBrick : function(brick_id)
34625 for (var i = 0; i<this.bricks.length; i++) {
34626 if (this.bricks[i].id == brick_id) {
34627 this.bricks.splice(i,1);
34628 this.el.dom.removeChild(Roo.get(brick_id).dom);
34635 * adds a Masonry Brick
34636 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34638 addBrick : function(cfg)
34640 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34641 //this.register(cn);
34642 cn.parentId = this.id;
34643 cn.render(this.el);
34648 * register a Masonry Brick
34649 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34652 register : function(brick)
34654 this.bricks.push(brick);
34655 brick.masonryId = this.id;
34659 * clear all the Masonry Brick
34661 clearAll : function()
34664 //this.getChildContainer().dom.innerHTML = "";
34665 this.el.dom.innerHTML = '';
34668 getSelected : function()
34670 if (!this.selectedBrick) {
34674 return this.selectedBrick;
34678 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34682 * register a Masonry Layout
34683 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34686 register : function(layout)
34688 this.groups[layout.id] = layout;
34691 * fetch a Masonry Layout based on the masonry layout ID
34692 * @param {string} the masonry layout to add
34693 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34696 get: function(layout_id) {
34697 if (typeof(this.groups[layout_id]) == 'undefined') {
34700 return this.groups[layout_id] ;
34712 * http://masonry.desandro.com
34714 * The idea is to render all the bricks based on vertical width...
34716 * The original code extends 'outlayer' - we might need to use that....
34722 * @class Roo.bootstrap.LayoutMasonryAuto
34723 * @extends Roo.bootstrap.Component
34724 * Bootstrap Layout Masonry class
34727 * Create a new Element
34728 * @param {Object} config The config object
34731 Roo.bootstrap.LayoutMasonryAuto = function(config){
34732 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34735 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34738 * @cfg {Boolean} isFitWidth - resize the width..
34740 isFitWidth : false, // options..
34742 * @cfg {Boolean} isOriginLeft = left align?
34744 isOriginLeft : true,
34746 * @cfg {Boolean} isOriginTop = top align?
34748 isOriginTop : false,
34750 * @cfg {Boolean} isLayoutInstant = no animation?
34752 isLayoutInstant : false, // needed?
34754 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34756 isResizingContainer : true,
34758 * @cfg {Number} columnWidth width of the columns
34764 * @cfg {Number} maxCols maximum number of columns
34769 * @cfg {Number} padHeight padding below box..
34775 * @cfg {Boolean} isAutoInitial defalut true
34778 isAutoInitial : true,
34784 initialColumnWidth : 0,
34785 currentSize : null,
34787 colYs : null, // array.
34794 bricks: null, //CompositeElement
34795 cols : 0, // array?
34796 // element : null, // wrapped now this.el
34797 _isLayoutInited : null,
34800 getAutoCreate : function(){
34804 cls: 'blog-masonary-wrapper ' + this.cls,
34806 cls : 'mas-boxes masonary'
34813 getChildContainer: function( )
34815 if (this.boxesEl) {
34816 return this.boxesEl;
34819 this.boxesEl = this.el.select('.mas-boxes').first();
34821 return this.boxesEl;
34825 initEvents : function()
34829 if(this.isAutoInitial){
34830 Roo.log('hook children rendered');
34831 this.on('childrenrendered', function() {
34832 Roo.log('children rendered');
34839 initial : function()
34841 this.reloadItems();
34843 this.currentSize = this.el.getBox(true);
34845 /// was window resize... - let's see if this works..
34846 Roo.EventManager.onWindowResize(this.resize, this);
34848 if(!this.isAutoInitial){
34853 this.layout.defer(500,this);
34856 reloadItems: function()
34858 this.bricks = this.el.select('.masonry-brick', true);
34860 this.bricks.each(function(b) {
34861 //Roo.log(b.getSize());
34862 if (!b.attr('originalwidth')) {
34863 b.attr('originalwidth', b.getSize().width);
34868 Roo.log(this.bricks.elements.length);
34871 resize : function()
34874 var cs = this.el.getBox(true);
34876 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34877 Roo.log("no change in with or X");
34880 this.currentSize = cs;
34884 layout : function()
34887 this._resetLayout();
34888 //this._manageStamps();
34890 // don't animate first layout
34891 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34892 this.layoutItems( isInstant );
34894 // flag for initalized
34895 this._isLayoutInited = true;
34898 layoutItems : function( isInstant )
34900 //var items = this._getItemsForLayout( this.items );
34901 // original code supports filtering layout items.. we just ignore it..
34903 this._layoutItems( this.bricks , isInstant );
34905 this._postLayout();
34907 _layoutItems : function ( items , isInstant)
34909 //this.fireEvent( 'layout', this, items );
34912 if ( !items || !items.elements.length ) {
34913 // no items, emit event with empty array
34918 items.each(function(item) {
34919 Roo.log("layout item");
34921 // get x/y object from method
34922 var position = this._getItemLayoutPosition( item );
34924 position.item = item;
34925 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34926 queue.push( position );
34929 this._processLayoutQueue( queue );
34931 /** Sets position of item in DOM
34932 * @param {Element} item
34933 * @param {Number} x - horizontal position
34934 * @param {Number} y - vertical position
34935 * @param {Boolean} isInstant - disables transitions
34937 _processLayoutQueue : function( queue )
34939 for ( var i=0, len = queue.length; i < len; i++ ) {
34940 var obj = queue[i];
34941 obj.item.position('absolute');
34942 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34948 * Any logic you want to do after each layout,
34949 * i.e. size the container
34951 _postLayout : function()
34953 this.resizeContainer();
34956 resizeContainer : function()
34958 if ( !this.isResizingContainer ) {
34961 var size = this._getContainerSize();
34963 this.el.setSize(size.width,size.height);
34964 this.boxesEl.setSize(size.width,size.height);
34970 _resetLayout : function()
34972 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34973 this.colWidth = this.el.getWidth();
34974 //this.gutter = this.el.getWidth();
34976 this.measureColumns();
34982 this.colYs.push( 0 );
34988 measureColumns : function()
34990 this.getContainerWidth();
34991 // if columnWidth is 0, default to outerWidth of first item
34992 if ( !this.columnWidth ) {
34993 var firstItem = this.bricks.first();
34994 Roo.log(firstItem);
34995 this.columnWidth = this.containerWidth;
34996 if (firstItem && firstItem.attr('originalwidth') ) {
34997 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34999 // columnWidth fall back to item of first element
35000 Roo.log("set column width?");
35001 this.initialColumnWidth = this.columnWidth ;
35003 // if first elem has no width, default to size of container
35008 if (this.initialColumnWidth) {
35009 this.columnWidth = this.initialColumnWidth;
35014 // column width is fixed at the top - however if container width get's smaller we should
35017 // this bit calcs how man columns..
35019 var columnWidth = this.columnWidth += this.gutter;
35021 // calculate columns
35022 var containerWidth = this.containerWidth + this.gutter;
35024 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35025 // fix rounding errors, typically with gutters
35026 var excess = columnWidth - containerWidth % columnWidth;
35029 // if overshoot is less than a pixel, round up, otherwise floor it
35030 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35031 cols = Math[ mathMethod ]( cols );
35032 this.cols = Math.max( cols, 1 );
35033 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35035 // padding positioning..
35036 var totalColWidth = this.cols * this.columnWidth;
35037 var padavail = this.containerWidth - totalColWidth;
35038 // so for 2 columns - we need 3 'pads'
35040 var padNeeded = (1+this.cols) * this.padWidth;
35042 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35044 this.columnWidth += padExtra
35045 //this.padWidth = Math.floor(padavail / ( this.cols));
35047 // adjust colum width so that padding is fixed??
35049 // we have 3 columns ... total = width * 3
35050 // we have X left over... that should be used by
35052 //if (this.expandC) {
35060 getContainerWidth : function()
35062 /* // container is parent if fit width
35063 var container = this.isFitWidth ? this.element.parentNode : this.element;
35064 // check that this.size and size are there
35065 // IE8 triggers resize on body size change, so they might not be
35067 var size = getSize( container ); //FIXME
35068 this.containerWidth = size && size.innerWidth; //FIXME
35071 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35075 _getItemLayoutPosition : function( item ) // what is item?
35077 // we resize the item to our columnWidth..
35079 item.setWidth(this.columnWidth);
35080 item.autoBoxAdjust = false;
35082 var sz = item.getSize();
35084 // how many columns does this brick span
35085 var remainder = this.containerWidth % this.columnWidth;
35087 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35088 // round if off by 1 pixel, otherwise use ceil
35089 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35090 colSpan = Math.min( colSpan, this.cols );
35092 // normally this should be '1' as we dont' currently allow multi width columns..
35094 var colGroup = this._getColGroup( colSpan );
35095 // get the minimum Y value from the columns
35096 var minimumY = Math.min.apply( Math, colGroup );
35097 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35099 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35101 // position the brick
35103 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35104 y: this.currentSize.y + minimumY + this.padHeight
35108 // apply setHeight to necessary columns
35109 var setHeight = minimumY + sz.height + this.padHeight;
35110 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35112 var setSpan = this.cols + 1 - colGroup.length;
35113 for ( var i = 0; i < setSpan; i++ ) {
35114 this.colYs[ shortColIndex + i ] = setHeight ;
35121 * @param {Number} colSpan - number of columns the element spans
35122 * @returns {Array} colGroup
35124 _getColGroup : function( colSpan )
35126 if ( colSpan < 2 ) {
35127 // if brick spans only one column, use all the column Ys
35132 // how many different places could this brick fit horizontally
35133 var groupCount = this.cols + 1 - colSpan;
35134 // for each group potential horizontal position
35135 for ( var i = 0; i < groupCount; i++ ) {
35136 // make an array of colY values for that one group
35137 var groupColYs = this.colYs.slice( i, i + colSpan );
35138 // and get the max value of the array
35139 colGroup[i] = Math.max.apply( Math, groupColYs );
35144 _manageStamp : function( stamp )
35146 var stampSize = stamp.getSize();
35147 var offset = stamp.getBox();
35148 // get the columns that this stamp affects
35149 var firstX = this.isOriginLeft ? offset.x : offset.right;
35150 var lastX = firstX + stampSize.width;
35151 var firstCol = Math.floor( firstX / this.columnWidth );
35152 firstCol = Math.max( 0, firstCol );
35154 var lastCol = Math.floor( lastX / this.columnWidth );
35155 // lastCol should not go over if multiple of columnWidth #425
35156 lastCol -= lastX % this.columnWidth ? 0 : 1;
35157 lastCol = Math.min( this.cols - 1, lastCol );
35159 // set colYs to bottom of the stamp
35160 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35163 for ( var i = firstCol; i <= lastCol; i++ ) {
35164 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35169 _getContainerSize : function()
35171 this.maxY = Math.max.apply( Math, this.colYs );
35176 if ( this.isFitWidth ) {
35177 size.width = this._getContainerFitWidth();
35183 _getContainerFitWidth : function()
35185 var unusedCols = 0;
35186 // count unused columns
35189 if ( this.colYs[i] !== 0 ) {
35194 // fit container to columns that have been used
35195 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35198 needsResizeLayout : function()
35200 var previousWidth = this.containerWidth;
35201 this.getContainerWidth();
35202 return previousWidth !== this.containerWidth;
35217 * @class Roo.bootstrap.MasonryBrick
35218 * @extends Roo.bootstrap.Component
35219 * Bootstrap MasonryBrick class
35222 * Create a new MasonryBrick
35223 * @param {Object} config The config object
35226 Roo.bootstrap.MasonryBrick = function(config){
35228 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35230 Roo.bootstrap.MasonryBrick.register(this);
35236 * When a MasonryBrick is clcik
35237 * @param {Roo.bootstrap.MasonryBrick} this
35238 * @param {Roo.EventObject} e
35244 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35247 * @cfg {String} title
35251 * @cfg {String} html
35255 * @cfg {String} bgimage
35259 * @cfg {String} videourl
35263 * @cfg {String} cls
35267 * @cfg {String} href
35271 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35276 * @cfg {String} placetitle (center|bottom)
35281 * @cfg {Boolean} isFitContainer defalut true
35283 isFitContainer : true,
35286 * @cfg {Boolean} preventDefault defalut false
35288 preventDefault : false,
35291 * @cfg {Boolean} inverse defalut false
35293 maskInverse : false,
35295 getAutoCreate : function()
35297 if(!this.isFitContainer){
35298 return this.getSplitAutoCreate();
35301 var cls = 'masonry-brick masonry-brick-full';
35303 if(this.href.length){
35304 cls += ' masonry-brick-link';
35307 if(this.bgimage.length){
35308 cls += ' masonry-brick-image';
35311 if(this.maskInverse){
35312 cls += ' mask-inverse';
35315 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35316 cls += ' enable-mask';
35320 cls += ' masonry-' + this.size + '-brick';
35323 if(this.placetitle.length){
35325 switch (this.placetitle) {
35327 cls += ' masonry-center-title';
35330 cls += ' masonry-bottom-title';
35337 if(!this.html.length && !this.bgimage.length){
35338 cls += ' masonry-center-title';
35341 if(!this.html.length && this.bgimage.length){
35342 cls += ' masonry-bottom-title';
35347 cls += ' ' + this.cls;
35351 tag: (this.href.length) ? 'a' : 'div',
35356 cls: 'masonry-brick-mask'
35360 cls: 'masonry-brick-paragraph',
35366 if(this.href.length){
35367 cfg.href = this.href;
35370 var cn = cfg.cn[1].cn;
35372 if(this.title.length){
35375 cls: 'masonry-brick-title',
35380 if(this.html.length){
35383 cls: 'masonry-brick-text',
35388 if (!this.title.length && !this.html.length) {
35389 cfg.cn[1].cls += ' hide';
35392 if(this.bgimage.length){
35395 cls: 'masonry-brick-image-view',
35400 if(this.videourl.length){
35401 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35402 // youtube support only?
35405 cls: 'masonry-brick-image-view',
35408 allowfullscreen : true
35416 getSplitAutoCreate : function()
35418 var cls = 'masonry-brick masonry-brick-split';
35420 if(this.href.length){
35421 cls += ' masonry-brick-link';
35424 if(this.bgimage.length){
35425 cls += ' masonry-brick-image';
35429 cls += ' masonry-' + this.size + '-brick';
35432 switch (this.placetitle) {
35434 cls += ' masonry-center-title';
35437 cls += ' masonry-bottom-title';
35440 if(!this.bgimage.length){
35441 cls += ' masonry-center-title';
35444 if(this.bgimage.length){
35445 cls += ' masonry-bottom-title';
35451 cls += ' ' + this.cls;
35455 tag: (this.href.length) ? 'a' : 'div',
35460 cls: 'masonry-brick-split-head',
35464 cls: 'masonry-brick-paragraph',
35471 cls: 'masonry-brick-split-body',
35477 if(this.href.length){
35478 cfg.href = this.href;
35481 if(this.title.length){
35482 cfg.cn[0].cn[0].cn.push({
35484 cls: 'masonry-brick-title',
35489 if(this.html.length){
35490 cfg.cn[1].cn.push({
35492 cls: 'masonry-brick-text',
35497 if(this.bgimage.length){
35498 cfg.cn[0].cn.push({
35500 cls: 'masonry-brick-image-view',
35505 if(this.videourl.length){
35506 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35507 // youtube support only?
35508 cfg.cn[0].cn.cn.push({
35510 cls: 'masonry-brick-image-view',
35513 allowfullscreen : true
35520 initEvents: function()
35522 switch (this.size) {
35555 this.el.on('touchstart', this.onTouchStart, this);
35556 this.el.on('touchmove', this.onTouchMove, this);
35557 this.el.on('touchend', this.onTouchEnd, this);
35558 this.el.on('contextmenu', this.onContextMenu, this);
35560 this.el.on('mouseenter' ,this.enter, this);
35561 this.el.on('mouseleave', this.leave, this);
35562 this.el.on('click', this.onClick, this);
35565 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35566 this.parent().bricks.push(this);
35571 onClick: function(e, el)
35573 var time = this.endTimer - this.startTimer;
35574 // Roo.log(e.preventDefault());
35577 e.preventDefault();
35582 if(!this.preventDefault){
35586 e.preventDefault();
35588 if (this.activeClass != '') {
35589 this.selectBrick();
35592 this.fireEvent('click', this, e);
35595 enter: function(e, el)
35597 e.preventDefault();
35599 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35603 if(this.bgimage.length && this.html.length){
35604 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35608 leave: function(e, el)
35610 e.preventDefault();
35612 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35616 if(this.bgimage.length && this.html.length){
35617 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35621 onTouchStart: function(e, el)
35623 // e.preventDefault();
35625 this.touchmoved = false;
35627 if(!this.isFitContainer){
35631 if(!this.bgimage.length || !this.html.length){
35635 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35637 this.timer = new Date().getTime();
35641 onTouchMove: function(e, el)
35643 this.touchmoved = true;
35646 onContextMenu : function(e,el)
35648 e.preventDefault();
35649 e.stopPropagation();
35653 onTouchEnd: function(e, el)
35655 // e.preventDefault();
35657 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35664 if(!this.bgimage.length || !this.html.length){
35666 if(this.href.length){
35667 window.location.href = this.href;
35673 if(!this.isFitContainer){
35677 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35679 window.location.href = this.href;
35682 //selection on single brick only
35683 selectBrick : function() {
35685 if (!this.parentId) {
35689 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35690 var index = m.selectedBrick.indexOf(this.id);
35693 m.selectedBrick.splice(index,1);
35694 this.el.removeClass(this.activeClass);
35698 for(var i = 0; i < m.selectedBrick.length; i++) {
35699 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35700 b.el.removeClass(b.activeClass);
35703 m.selectedBrick = [];
35705 m.selectedBrick.push(this.id);
35706 this.el.addClass(this.activeClass);
35710 isSelected : function(){
35711 return this.el.hasClass(this.activeClass);
35716 Roo.apply(Roo.bootstrap.MasonryBrick, {
35719 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35721 * register a Masonry Brick
35722 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35725 register : function(brick)
35727 //this.groups[brick.id] = brick;
35728 this.groups.add(brick.id, brick);
35731 * fetch a masonry brick based on the masonry brick ID
35732 * @param {string} the masonry brick to add
35733 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35736 get: function(brick_id)
35738 // if (typeof(this.groups[brick_id]) == 'undefined') {
35741 // return this.groups[brick_id] ;
35743 if(this.groups.key(brick_id)) {
35744 return this.groups.key(brick_id);
35762 * @class Roo.bootstrap.Brick
35763 * @extends Roo.bootstrap.Component
35764 * Bootstrap Brick class
35767 * Create a new Brick
35768 * @param {Object} config The config object
35771 Roo.bootstrap.Brick = function(config){
35772 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35778 * When a Brick is click
35779 * @param {Roo.bootstrap.Brick} this
35780 * @param {Roo.EventObject} e
35786 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35789 * @cfg {String} title
35793 * @cfg {String} html
35797 * @cfg {String} bgimage
35801 * @cfg {String} cls
35805 * @cfg {String} href
35809 * @cfg {String} video
35813 * @cfg {Boolean} square
35817 getAutoCreate : function()
35819 var cls = 'roo-brick';
35821 if(this.href.length){
35822 cls += ' roo-brick-link';
35825 if(this.bgimage.length){
35826 cls += ' roo-brick-image';
35829 if(!this.html.length && !this.bgimage.length){
35830 cls += ' roo-brick-center-title';
35833 if(!this.html.length && this.bgimage.length){
35834 cls += ' roo-brick-bottom-title';
35838 cls += ' ' + this.cls;
35842 tag: (this.href.length) ? 'a' : 'div',
35847 cls: 'roo-brick-paragraph',
35853 if(this.href.length){
35854 cfg.href = this.href;
35857 var cn = cfg.cn[0].cn;
35859 if(this.title.length){
35862 cls: 'roo-brick-title',
35867 if(this.html.length){
35870 cls: 'roo-brick-text',
35877 if(this.bgimage.length){
35880 cls: 'roo-brick-image-view',
35888 initEvents: function()
35890 if(this.title.length || this.html.length){
35891 this.el.on('mouseenter' ,this.enter, this);
35892 this.el.on('mouseleave', this.leave, this);
35895 Roo.EventManager.onWindowResize(this.resize, this);
35897 if(this.bgimage.length){
35898 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35899 this.imageEl.on('load', this.onImageLoad, this);
35906 onImageLoad : function()
35911 resize : function()
35913 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35915 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35917 if(this.bgimage.length){
35918 var image = this.el.select('.roo-brick-image-view', true).first();
35920 image.setWidth(paragraph.getWidth());
35923 image.setHeight(paragraph.getWidth());
35926 this.el.setHeight(image.getHeight());
35927 paragraph.setHeight(image.getHeight());
35933 enter: function(e, el)
35935 e.preventDefault();
35937 if(this.bgimage.length){
35938 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35939 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35943 leave: function(e, el)
35945 e.preventDefault();
35947 if(this.bgimage.length){
35948 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35949 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35964 * @class Roo.bootstrap.NumberField
35965 * @extends Roo.bootstrap.Input
35966 * Bootstrap NumberField class
35972 * Create a new NumberField
35973 * @param {Object} config The config object
35976 Roo.bootstrap.NumberField = function(config){
35977 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35980 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35983 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35985 allowDecimals : true,
35987 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35989 decimalSeparator : ".",
35991 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35993 decimalPrecision : 2,
35995 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35997 allowNegative : true,
36000 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36004 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36006 minValue : Number.NEGATIVE_INFINITY,
36008 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36010 maxValue : Number.MAX_VALUE,
36012 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36014 minText : "The minimum value for this field is {0}",
36016 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36018 maxText : "The maximum value for this field is {0}",
36020 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36021 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36023 nanText : "{0} is not a valid number",
36025 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36027 thousandsDelimiter : false,
36029 * @cfg {String} valueAlign alignment of value
36031 valueAlign : "left",
36033 getAutoCreate : function()
36035 var hiddenInput = {
36039 cls: 'hidden-number-input'
36043 hiddenInput.name = this.name;
36048 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36050 this.name = hiddenInput.name;
36052 if(cfg.cn.length > 0) {
36053 cfg.cn.push(hiddenInput);
36060 initEvents : function()
36062 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36064 var allowed = "0123456789";
36066 if(this.allowDecimals){
36067 allowed += this.decimalSeparator;
36070 if(this.allowNegative){
36074 if(this.thousandsDelimiter) {
36078 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36080 var keyPress = function(e){
36082 var k = e.getKey();
36084 var c = e.getCharCode();
36087 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36088 allowed.indexOf(String.fromCharCode(c)) === -1
36094 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36098 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36103 this.el.on("keypress", keyPress, this);
36106 validateValue : function(value)
36109 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36113 var num = this.parseValue(value);
36116 this.markInvalid(String.format(this.nanText, value));
36120 if(num < this.minValue){
36121 this.markInvalid(String.format(this.minText, this.minValue));
36125 if(num > this.maxValue){
36126 this.markInvalid(String.format(this.maxText, this.maxValue));
36133 getValue : function()
36135 var v = this.hiddenEl().getValue();
36137 return this.fixPrecision(this.parseValue(v));
36140 parseValue : function(value)
36142 if(this.thousandsDelimiter) {
36144 r = new RegExp(",", "g");
36145 value = value.replace(r, "");
36148 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36149 return isNaN(value) ? '' : value;
36152 fixPrecision : function(value)
36154 if(this.thousandsDelimiter) {
36156 r = new RegExp(",", "g");
36157 value = value.replace(r, "");
36160 var nan = isNaN(value);
36162 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36163 return nan ? '' : value;
36165 return parseFloat(value).toFixed(this.decimalPrecision);
36168 setValue : function(v)
36170 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36176 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36178 this.inputEl().dom.value = (v == '') ? '' :
36179 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36181 if(!this.allowZero && v === '0') {
36182 this.hiddenEl().dom.value = '';
36183 this.inputEl().dom.value = '';
36190 decimalPrecisionFcn : function(v)
36192 return Math.floor(v);
36195 beforeBlur : function()
36197 var v = this.parseValue(this.getRawValue());
36199 if(v || v === 0 || v === ''){
36204 hiddenEl : function()
36206 return this.el.select('input.hidden-number-input',true).first();
36218 * @class Roo.bootstrap.DocumentSlider
36219 * @extends Roo.bootstrap.Component
36220 * Bootstrap DocumentSlider class
36223 * Create a new DocumentViewer
36224 * @param {Object} config The config object
36227 Roo.bootstrap.DocumentSlider = function(config){
36228 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36235 * Fire after initEvent
36236 * @param {Roo.bootstrap.DocumentSlider} this
36241 * Fire after update
36242 * @param {Roo.bootstrap.DocumentSlider} this
36248 * @param {Roo.bootstrap.DocumentSlider} this
36254 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36260 getAutoCreate : function()
36264 cls : 'roo-document-slider',
36268 cls : 'roo-document-slider-header',
36272 cls : 'roo-document-slider-header-title'
36278 cls : 'roo-document-slider-body',
36282 cls : 'roo-document-slider-prev',
36286 cls : 'fa fa-chevron-left'
36292 cls : 'roo-document-slider-thumb',
36296 cls : 'roo-document-slider-image'
36302 cls : 'roo-document-slider-next',
36306 cls : 'fa fa-chevron-right'
36318 initEvents : function()
36320 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36321 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36323 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36324 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36326 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36327 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36329 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36330 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36332 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36333 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36335 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36336 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36338 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36339 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36341 this.thumbEl.on('click', this.onClick, this);
36343 this.prevIndicator.on('click', this.prev, this);
36345 this.nextIndicator.on('click', this.next, this);
36349 initial : function()
36351 if(this.files.length){
36352 this.indicator = 1;
36356 this.fireEvent('initial', this);
36359 update : function()
36361 this.imageEl.attr('src', this.files[this.indicator - 1]);
36363 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36365 this.prevIndicator.show();
36367 if(this.indicator == 1){
36368 this.prevIndicator.hide();
36371 this.nextIndicator.show();
36373 if(this.indicator == this.files.length){
36374 this.nextIndicator.hide();
36377 this.thumbEl.scrollTo('top');
36379 this.fireEvent('update', this);
36382 onClick : function(e)
36384 e.preventDefault();
36386 this.fireEvent('click', this);
36391 e.preventDefault();
36393 this.indicator = Math.max(1, this.indicator - 1);
36400 e.preventDefault();
36402 this.indicator = Math.min(this.files.length, this.indicator + 1);
36416 * @class Roo.bootstrap.RadioSet
36417 * @extends Roo.bootstrap.Input
36418 * Bootstrap RadioSet class
36419 * @cfg {String} indicatorpos (left|right) default left
36420 * @cfg {Boolean} inline (true|false) inline the element (default true)
36421 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36423 * Create a new RadioSet
36424 * @param {Object} config The config object
36427 Roo.bootstrap.RadioSet = function(config){
36429 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36433 Roo.bootstrap.RadioSet.register(this);
36438 * Fires when the element is checked or unchecked.
36439 * @param {Roo.bootstrap.RadioSet} this This radio
36440 * @param {Roo.bootstrap.Radio} item The checked item
36445 * Fires when the element is click.
36446 * @param {Roo.bootstrap.RadioSet} this This radio set
36447 * @param {Roo.bootstrap.Radio} item The checked item
36448 * @param {Roo.EventObject} e The event object
36455 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36463 indicatorpos : 'left',
36465 getAutoCreate : function()
36469 cls : 'roo-radio-set-label',
36473 html : this.fieldLabel
36477 if (Roo.bootstrap.version == 3) {
36480 if(this.indicatorpos == 'left'){
36483 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36484 tooltip : 'This field is required'
36489 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36490 tooltip : 'This field is required'
36496 cls : 'roo-radio-set-items'
36499 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36501 if (align === 'left' && this.fieldLabel.length) {
36504 cls : "roo-radio-set-right",
36510 if(this.labelWidth > 12){
36511 label.style = "width: " + this.labelWidth + 'px';
36514 if(this.labelWidth < 13 && this.labelmd == 0){
36515 this.labelmd = this.labelWidth;
36518 if(this.labellg > 0){
36519 label.cls += ' col-lg-' + this.labellg;
36520 items.cls += ' col-lg-' + (12 - this.labellg);
36523 if(this.labelmd > 0){
36524 label.cls += ' col-md-' + this.labelmd;
36525 items.cls += ' col-md-' + (12 - this.labelmd);
36528 if(this.labelsm > 0){
36529 label.cls += ' col-sm-' + this.labelsm;
36530 items.cls += ' col-sm-' + (12 - this.labelsm);
36533 if(this.labelxs > 0){
36534 label.cls += ' col-xs-' + this.labelxs;
36535 items.cls += ' col-xs-' + (12 - this.labelxs);
36541 cls : 'roo-radio-set',
36545 cls : 'roo-radio-set-input',
36548 value : this.value ? this.value : ''
36555 if(this.weight.length){
36556 cfg.cls += ' roo-radio-' + this.weight;
36560 cfg.cls += ' roo-radio-set-inline';
36564 ['xs','sm','md','lg'].map(function(size){
36565 if (settings[size]) {
36566 cfg.cls += ' col-' + size + '-' + settings[size];
36574 initEvents : function()
36576 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36577 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36579 if(!this.fieldLabel.length){
36580 this.labelEl.hide();
36583 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36584 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36586 this.indicator = this.indicatorEl();
36588 if(this.indicator){
36589 this.indicator.addClass('invisible');
36592 this.originalValue = this.getValue();
36596 inputEl: function ()
36598 return this.el.select('.roo-radio-set-input', true).first();
36601 getChildContainer : function()
36603 return this.itemsEl;
36606 register : function(item)
36608 this.radioes.push(item);
36612 validate : function()
36614 if(this.getVisibilityEl().hasClass('hidden')){
36620 Roo.each(this.radioes, function(i){
36629 if(this.allowBlank) {
36633 if(this.disabled || valid){
36638 this.markInvalid();
36643 markValid : function()
36645 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36646 this.indicatorEl().removeClass('visible');
36647 this.indicatorEl().addClass('invisible');
36651 if (Roo.bootstrap.version == 3) {
36652 this.el.removeClass([this.invalidClass, this.validClass]);
36653 this.el.addClass(this.validClass);
36655 this.el.removeClass(['is-invalid','is-valid']);
36656 this.el.addClass(['is-valid']);
36658 this.fireEvent('valid', this);
36661 markInvalid : function(msg)
36663 if(this.allowBlank || this.disabled){
36667 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36668 this.indicatorEl().removeClass('invisible');
36669 this.indicatorEl().addClass('visible');
36671 if (Roo.bootstrap.version == 3) {
36672 this.el.removeClass([this.invalidClass, this.validClass]);
36673 this.el.addClass(this.invalidClass);
36675 this.el.removeClass(['is-invalid','is-valid']);
36676 this.el.addClass(['is-invalid']);
36679 this.fireEvent('invalid', this, msg);
36683 setValue : function(v, suppressEvent)
36685 if(this.value === v){
36692 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36695 Roo.each(this.radioes, function(i){
36697 i.el.removeClass('checked');
36700 Roo.each(this.radioes, function(i){
36702 if(i.value === v || i.value.toString() === v.toString()){
36704 i.el.addClass('checked');
36706 if(suppressEvent !== true){
36707 this.fireEvent('check', this, i);
36718 clearInvalid : function(){
36720 if(!this.el || this.preventMark){
36724 this.el.removeClass([this.invalidClass]);
36726 this.fireEvent('valid', this);
36731 Roo.apply(Roo.bootstrap.RadioSet, {
36735 register : function(set)
36737 this.groups[set.name] = set;
36740 get: function(name)
36742 if (typeof(this.groups[name]) == 'undefined') {
36746 return this.groups[name] ;
36752 * Ext JS Library 1.1.1
36753 * Copyright(c) 2006-2007, Ext JS, LLC.
36755 * Originally Released Under LGPL - original licence link has changed is not relivant.
36758 * <script type="text/javascript">
36763 * @class Roo.bootstrap.SplitBar
36764 * @extends Roo.util.Observable
36765 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36769 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36770 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36771 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36772 split.minSize = 100;
36773 split.maxSize = 600;
36774 split.animate = true;
36775 split.on('moved', splitterMoved);
36778 * Create a new SplitBar
36779 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36780 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36781 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36782 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36783 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36784 position of the SplitBar).
36786 Roo.bootstrap.SplitBar = function(cfg){
36791 // dragElement : elm
36792 // resizingElement: el,
36794 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36795 // placement : Roo.bootstrap.SplitBar.LEFT ,
36796 // existingProxy ???
36799 this.el = Roo.get(cfg.dragElement, true);
36800 this.el.dom.unselectable = "on";
36802 this.resizingEl = Roo.get(cfg.resizingElement, true);
36806 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36807 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36810 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36813 * The minimum size of the resizing element. (Defaults to 0)
36819 * The maximum size of the resizing element. (Defaults to 2000)
36822 this.maxSize = 2000;
36825 * Whether to animate the transition to the new size
36828 this.animate = false;
36831 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36834 this.useShim = false;
36839 if(!cfg.existingProxy){
36841 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36843 this.proxy = Roo.get(cfg.existingProxy).dom;
36846 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36849 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36852 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36855 this.dragSpecs = {};
36858 * @private The adapter to use to positon and resize elements
36860 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36861 this.adapter.init(this);
36863 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36865 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36866 this.el.addClass("roo-splitbar-h");
36869 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36870 this.el.addClass("roo-splitbar-v");
36876 * Fires when the splitter is moved (alias for {@link #event-moved})
36877 * @param {Roo.bootstrap.SplitBar} this
36878 * @param {Number} newSize the new width or height
36883 * Fires when the splitter is moved
36884 * @param {Roo.bootstrap.SplitBar} this
36885 * @param {Number} newSize the new width or height
36889 * @event beforeresize
36890 * Fires before the splitter is dragged
36891 * @param {Roo.bootstrap.SplitBar} this
36893 "beforeresize" : true,
36895 "beforeapply" : true
36898 Roo.util.Observable.call(this);
36901 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36902 onStartProxyDrag : function(x, y){
36903 this.fireEvent("beforeresize", this);
36905 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36907 o.enableDisplayMode("block");
36908 // all splitbars share the same overlay
36909 Roo.bootstrap.SplitBar.prototype.overlay = o;
36911 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36912 this.overlay.show();
36913 Roo.get(this.proxy).setDisplayed("block");
36914 var size = this.adapter.getElementSize(this);
36915 this.activeMinSize = this.getMinimumSize();;
36916 this.activeMaxSize = this.getMaximumSize();;
36917 var c1 = size - this.activeMinSize;
36918 var c2 = Math.max(this.activeMaxSize - size, 0);
36919 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36920 this.dd.resetConstraints();
36921 this.dd.setXConstraint(
36922 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36923 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36925 this.dd.setYConstraint(0, 0);
36927 this.dd.resetConstraints();
36928 this.dd.setXConstraint(0, 0);
36929 this.dd.setYConstraint(
36930 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36931 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36934 this.dragSpecs.startSize = size;
36935 this.dragSpecs.startPoint = [x, y];
36936 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36940 * @private Called after the drag operation by the DDProxy
36942 onEndProxyDrag : function(e){
36943 Roo.get(this.proxy).setDisplayed(false);
36944 var endPoint = Roo.lib.Event.getXY(e);
36946 this.overlay.hide();
36949 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36950 newSize = this.dragSpecs.startSize +
36951 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36952 endPoint[0] - this.dragSpecs.startPoint[0] :
36953 this.dragSpecs.startPoint[0] - endPoint[0]
36956 newSize = this.dragSpecs.startSize +
36957 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36958 endPoint[1] - this.dragSpecs.startPoint[1] :
36959 this.dragSpecs.startPoint[1] - endPoint[1]
36962 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36963 if(newSize != this.dragSpecs.startSize){
36964 if(this.fireEvent('beforeapply', this, newSize) !== false){
36965 this.adapter.setElementSize(this, newSize);
36966 this.fireEvent("moved", this, newSize);
36967 this.fireEvent("resize", this, newSize);
36973 * Get the adapter this SplitBar uses
36974 * @return The adapter object
36976 getAdapter : function(){
36977 return this.adapter;
36981 * Set the adapter this SplitBar uses
36982 * @param {Object} adapter A SplitBar adapter object
36984 setAdapter : function(adapter){
36985 this.adapter = adapter;
36986 this.adapter.init(this);
36990 * Gets the minimum size for the resizing element
36991 * @return {Number} The minimum size
36993 getMinimumSize : function(){
36994 return this.minSize;
36998 * Sets the minimum size for the resizing element
36999 * @param {Number} minSize The minimum size
37001 setMinimumSize : function(minSize){
37002 this.minSize = minSize;
37006 * Gets the maximum size for the resizing element
37007 * @return {Number} The maximum size
37009 getMaximumSize : function(){
37010 return this.maxSize;
37014 * Sets the maximum size for the resizing element
37015 * @param {Number} maxSize The maximum size
37017 setMaximumSize : function(maxSize){
37018 this.maxSize = maxSize;
37022 * Sets the initialize size for the resizing element
37023 * @param {Number} size The initial size
37025 setCurrentSize : function(size){
37026 var oldAnimate = this.animate;
37027 this.animate = false;
37028 this.adapter.setElementSize(this, size);
37029 this.animate = oldAnimate;
37033 * Destroy this splitbar.
37034 * @param {Boolean} removeEl True to remove the element
37036 destroy : function(removeEl){
37038 this.shim.remove();
37041 this.proxy.parentNode.removeChild(this.proxy);
37049 * @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.
37051 Roo.bootstrap.SplitBar.createProxy = function(dir){
37052 var proxy = new Roo.Element(document.createElement("div"));
37053 proxy.unselectable();
37054 var cls = 'roo-splitbar-proxy';
37055 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37056 document.body.appendChild(proxy.dom);
37061 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37062 * Default Adapter. It assumes the splitter and resizing element are not positioned
37063 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37065 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37068 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37069 // do nothing for now
37070 init : function(s){
37074 * Called before drag operations to get the current size of the resizing element.
37075 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37077 getElementSize : function(s){
37078 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37079 return s.resizingEl.getWidth();
37081 return s.resizingEl.getHeight();
37086 * Called after drag operations to set the size of the resizing element.
37087 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37088 * @param {Number} newSize The new size to set
37089 * @param {Function} onComplete A function to be invoked when resizing is complete
37091 setElementSize : function(s, newSize, onComplete){
37092 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37094 s.resizingEl.setWidth(newSize);
37096 onComplete(s, newSize);
37099 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37104 s.resizingEl.setHeight(newSize);
37106 onComplete(s, newSize);
37109 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37116 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37117 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37118 * Adapter that moves the splitter element to align with the resized sizing element.
37119 * Used with an absolute positioned SplitBar.
37120 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37121 * document.body, make sure you assign an id to the body element.
37123 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37124 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37125 this.container = Roo.get(container);
37128 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37129 init : function(s){
37130 this.basic.init(s);
37133 getElementSize : function(s){
37134 return this.basic.getElementSize(s);
37137 setElementSize : function(s, newSize, onComplete){
37138 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37141 moveSplitter : function(s){
37142 var yes = Roo.bootstrap.SplitBar;
37143 switch(s.placement){
37145 s.el.setX(s.resizingEl.getRight());
37148 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37151 s.el.setY(s.resizingEl.getBottom());
37154 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37161 * Orientation constant - Create a vertical SplitBar
37165 Roo.bootstrap.SplitBar.VERTICAL = 1;
37168 * Orientation constant - Create a horizontal SplitBar
37172 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37175 * Placement constant - The resizing element is to the left of the splitter element
37179 Roo.bootstrap.SplitBar.LEFT = 1;
37182 * Placement constant - The resizing element is to the right of the splitter element
37186 Roo.bootstrap.SplitBar.RIGHT = 2;
37189 * Placement constant - The resizing element is positioned above the splitter element
37193 Roo.bootstrap.SplitBar.TOP = 3;
37196 * Placement constant - The resizing element is positioned under splitter element
37200 Roo.bootstrap.SplitBar.BOTTOM = 4;
37201 Roo.namespace("Roo.bootstrap.layout");/*
37203 * Ext JS Library 1.1.1
37204 * Copyright(c) 2006-2007, Ext JS, LLC.
37206 * Originally Released Under LGPL - original licence link has changed is not relivant.
37209 * <script type="text/javascript">
37213 * @class Roo.bootstrap.layout.Manager
37214 * @extends Roo.bootstrap.Component
37215 * Base class for layout managers.
37217 Roo.bootstrap.layout.Manager = function(config)
37219 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37225 /** false to disable window resize monitoring @type Boolean */
37226 this.monitorWindowResize = true;
37231 * Fires when a layout is performed.
37232 * @param {Roo.LayoutManager} this
37236 * @event regionresized
37237 * Fires when the user resizes a region.
37238 * @param {Roo.LayoutRegion} region The resized region
37239 * @param {Number} newSize The new size (width for east/west, height for north/south)
37241 "regionresized" : true,
37243 * @event regioncollapsed
37244 * Fires when a region is collapsed.
37245 * @param {Roo.LayoutRegion} region The collapsed region
37247 "regioncollapsed" : true,
37249 * @event regionexpanded
37250 * Fires when a region is expanded.
37251 * @param {Roo.LayoutRegion} region The expanded region
37253 "regionexpanded" : true
37255 this.updating = false;
37258 this.el = Roo.get(config.el);
37264 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37269 monitorWindowResize : true,
37275 onRender : function(ct, position)
37278 this.el = Roo.get(ct);
37281 //this.fireEvent('render',this);
37285 initEvents: function()
37289 // ie scrollbar fix
37290 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37291 document.body.scroll = "no";
37292 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37293 this.el.position('relative');
37295 this.id = this.el.id;
37296 this.el.addClass("roo-layout-container");
37297 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37298 if(this.el.dom != document.body ) {
37299 this.el.on('resize', this.layout,this);
37300 this.el.on('show', this.layout,this);
37306 * Returns true if this layout is currently being updated
37307 * @return {Boolean}
37309 isUpdating : function(){
37310 return this.updating;
37314 * Suspend the LayoutManager from doing auto-layouts while
37315 * making multiple add or remove calls
37317 beginUpdate : function(){
37318 this.updating = true;
37322 * Restore auto-layouts and optionally disable the manager from performing a layout
37323 * @param {Boolean} noLayout true to disable a layout update
37325 endUpdate : function(noLayout){
37326 this.updating = false;
37332 layout: function(){
37336 onRegionResized : function(region, newSize){
37337 this.fireEvent("regionresized", region, newSize);
37341 onRegionCollapsed : function(region){
37342 this.fireEvent("regioncollapsed", region);
37345 onRegionExpanded : function(region){
37346 this.fireEvent("regionexpanded", region);
37350 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37351 * performs box-model adjustments.
37352 * @return {Object} The size as an object {width: (the width), height: (the height)}
37354 getViewSize : function()
37357 if(this.el.dom != document.body){
37358 size = this.el.getSize();
37360 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37362 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37363 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37368 * Returns the Element this layout is bound to.
37369 * @return {Roo.Element}
37371 getEl : function(){
37376 * Returns the specified region.
37377 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37378 * @return {Roo.LayoutRegion}
37380 getRegion : function(target){
37381 return this.regions[target.toLowerCase()];
37384 onWindowResize : function(){
37385 if(this.monitorWindowResize){
37392 * Ext JS Library 1.1.1
37393 * Copyright(c) 2006-2007, Ext JS, LLC.
37395 * Originally Released Under LGPL - original licence link has changed is not relivant.
37398 * <script type="text/javascript">
37401 * @class Roo.bootstrap.layout.Border
37402 * @extends Roo.bootstrap.layout.Manager
37403 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37404 * please see: examples/bootstrap/nested.html<br><br>
37406 <b>The container the layout is rendered into can be either the body element or any other element.
37407 If it is not the body element, the container needs to either be an absolute positioned element,
37408 or you will need to add "position:relative" to the css of the container. You will also need to specify
37409 the container size if it is not the body element.</b>
37412 * Create a new Border
37413 * @param {Object} config Configuration options
37415 Roo.bootstrap.layout.Border = function(config){
37416 config = config || {};
37417 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37421 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37422 if(config[region]){
37423 config[region].region = region;
37424 this.addRegion(config[region]);
37430 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37432 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37434 parent : false, // this might point to a 'nest' or a ???
37437 * Creates and adds a new region if it doesn't already exist.
37438 * @param {String} target The target region key (north, south, east, west or center).
37439 * @param {Object} config The regions config object
37440 * @return {BorderLayoutRegion} The new region
37442 addRegion : function(config)
37444 if(!this.regions[config.region]){
37445 var r = this.factory(config);
37446 this.bindRegion(r);
37448 return this.regions[config.region];
37452 bindRegion : function(r){
37453 this.regions[r.config.region] = r;
37455 r.on("visibilitychange", this.layout, this);
37456 r.on("paneladded", this.layout, this);
37457 r.on("panelremoved", this.layout, this);
37458 r.on("invalidated", this.layout, this);
37459 r.on("resized", this.onRegionResized, this);
37460 r.on("collapsed", this.onRegionCollapsed, this);
37461 r.on("expanded", this.onRegionExpanded, this);
37465 * Performs a layout update.
37467 layout : function()
37469 if(this.updating) {
37473 // render all the rebions if they have not been done alreayd?
37474 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37475 if(this.regions[region] && !this.regions[region].bodyEl){
37476 this.regions[region].onRender(this.el)
37480 var size = this.getViewSize();
37481 var w = size.width;
37482 var h = size.height;
37487 //var x = 0, y = 0;
37489 var rs = this.regions;
37490 var north = rs["north"];
37491 var south = rs["south"];
37492 var west = rs["west"];
37493 var east = rs["east"];
37494 var center = rs["center"];
37495 //if(this.hideOnLayout){ // not supported anymore
37496 //c.el.setStyle("display", "none");
37498 if(north && north.isVisible()){
37499 var b = north.getBox();
37500 var m = north.getMargins();
37501 b.width = w - (m.left+m.right);
37504 centerY = b.height + b.y + m.bottom;
37505 centerH -= centerY;
37506 north.updateBox(this.safeBox(b));
37508 if(south && south.isVisible()){
37509 var b = south.getBox();
37510 var m = south.getMargins();
37511 b.width = w - (m.left+m.right);
37513 var totalHeight = (b.height + m.top + m.bottom);
37514 b.y = h - totalHeight + m.top;
37515 centerH -= totalHeight;
37516 south.updateBox(this.safeBox(b));
37518 if(west && west.isVisible()){
37519 var b = west.getBox();
37520 var m = west.getMargins();
37521 b.height = centerH - (m.top+m.bottom);
37523 b.y = centerY + m.top;
37524 var totalWidth = (b.width + m.left + m.right);
37525 centerX += totalWidth;
37526 centerW -= totalWidth;
37527 west.updateBox(this.safeBox(b));
37529 if(east && east.isVisible()){
37530 var b = east.getBox();
37531 var m = east.getMargins();
37532 b.height = centerH - (m.top+m.bottom);
37533 var totalWidth = (b.width + m.left + m.right);
37534 b.x = w - totalWidth + m.left;
37535 b.y = centerY + m.top;
37536 centerW -= totalWidth;
37537 east.updateBox(this.safeBox(b));
37540 var m = center.getMargins();
37542 x: centerX + m.left,
37543 y: centerY + m.top,
37544 width: centerW - (m.left+m.right),
37545 height: centerH - (m.top+m.bottom)
37547 //if(this.hideOnLayout){
37548 //center.el.setStyle("display", "block");
37550 center.updateBox(this.safeBox(centerBox));
37553 this.fireEvent("layout", this);
37557 safeBox : function(box){
37558 box.width = Math.max(0, box.width);
37559 box.height = Math.max(0, box.height);
37564 * Adds a ContentPanel (or subclass) to this layout.
37565 * @param {String} target The target region key (north, south, east, west or center).
37566 * @param {Roo.ContentPanel} panel The panel to add
37567 * @return {Roo.ContentPanel} The added panel
37569 add : function(target, panel){
37571 target = target.toLowerCase();
37572 return this.regions[target].add(panel);
37576 * Remove a ContentPanel (or subclass) to this layout.
37577 * @param {String} target The target region key (north, south, east, west or center).
37578 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37579 * @return {Roo.ContentPanel} The removed panel
37581 remove : function(target, panel){
37582 target = target.toLowerCase();
37583 return this.regions[target].remove(panel);
37587 * Searches all regions for a panel with the specified id
37588 * @param {String} panelId
37589 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37591 findPanel : function(panelId){
37592 var rs = this.regions;
37593 for(var target in rs){
37594 if(typeof rs[target] != "function"){
37595 var p = rs[target].getPanel(panelId);
37605 * Searches all regions for a panel with the specified id and activates (shows) it.
37606 * @param {String/ContentPanel} panelId The panels id or the panel itself
37607 * @return {Roo.ContentPanel} The shown panel or null
37609 showPanel : function(panelId) {
37610 var rs = this.regions;
37611 for(var target in rs){
37612 var r = rs[target];
37613 if(typeof r != "function"){
37614 if(r.hasPanel(panelId)){
37615 return r.showPanel(panelId);
37623 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37624 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37627 restoreState : function(provider){
37629 provider = Roo.state.Manager;
37631 var sm = new Roo.LayoutStateManager();
37632 sm.init(this, provider);
37638 * Adds a xtype elements to the layout.
37642 xtype : 'ContentPanel',
37649 xtype : 'NestedLayoutPanel',
37655 items : [ ... list of content panels or nested layout panels.. ]
37659 * @param {Object} cfg Xtype definition of item to add.
37661 addxtype : function(cfg)
37663 // basically accepts a pannel...
37664 // can accept a layout region..!?!?
37665 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37668 // theory? children can only be panels??
37670 //if (!cfg.xtype.match(/Panel$/)) {
37675 if (typeof(cfg.region) == 'undefined') {
37676 Roo.log("Failed to add Panel, region was not set");
37680 var region = cfg.region;
37686 xitems = cfg.items;
37691 if ( region == 'center') {
37692 Roo.log("Center: " + cfg.title);
37698 case 'Content': // ContentPanel (el, cfg)
37699 case 'Scroll': // ContentPanel (el, cfg)
37701 cfg.autoCreate = cfg.autoCreate || true;
37702 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37704 // var el = this.el.createChild();
37705 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37708 this.add(region, ret);
37712 case 'TreePanel': // our new panel!
37713 cfg.el = this.el.createChild();
37714 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37715 this.add(region, ret);
37720 // create a new Layout (which is a Border Layout...
37722 var clayout = cfg.layout;
37723 clayout.el = this.el.createChild();
37724 clayout.items = clayout.items || [];
37728 // replace this exitems with the clayout ones..
37729 xitems = clayout.items;
37731 // force background off if it's in center...
37732 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37733 cfg.background = false;
37735 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37738 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37739 //console.log('adding nested layout panel ' + cfg.toSource());
37740 this.add(region, ret);
37741 nb = {}; /// find first...
37746 // needs grid and region
37748 //var el = this.getRegion(region).el.createChild();
37750 *var el = this.el.createChild();
37751 // create the grid first...
37752 cfg.grid.container = el;
37753 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37756 if (region == 'center' && this.active ) {
37757 cfg.background = false;
37760 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37762 this.add(region, ret);
37764 if (cfg.background) {
37765 // render grid on panel activation (if panel background)
37766 ret.on('activate', function(gp) {
37767 if (!gp.grid.rendered) {
37768 // gp.grid.render(el);
37772 // cfg.grid.render(el);
37778 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37779 // it was the old xcomponent building that caused this before.
37780 // espeically if border is the top element in the tree.
37790 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37792 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37793 this.add(region, ret);
37797 throw "Can not add '" + cfg.xtype + "' to Border";
37803 this.beginUpdate();
37807 Roo.each(xitems, function(i) {
37808 region = nb && i.region ? i.region : false;
37810 var add = ret.addxtype(i);
37813 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37814 if (!i.background) {
37815 abn[region] = nb[region] ;
37822 // make the last non-background panel active..
37823 //if (nb) { Roo.log(abn); }
37826 for(var r in abn) {
37827 region = this.getRegion(r);
37829 // tried using nb[r], but it does not work..
37831 region.showPanel(abn[r]);
37842 factory : function(cfg)
37845 var validRegions = Roo.bootstrap.layout.Border.regions;
37847 var target = cfg.region;
37850 var r = Roo.bootstrap.layout;
37854 return new r.North(cfg);
37856 return new r.South(cfg);
37858 return new r.East(cfg);
37860 return new r.West(cfg);
37862 return new r.Center(cfg);
37864 throw 'Layout region "'+target+'" not supported.';
37871 * Ext JS Library 1.1.1
37872 * Copyright(c) 2006-2007, Ext JS, LLC.
37874 * Originally Released Under LGPL - original licence link has changed is not relivant.
37877 * <script type="text/javascript">
37881 * @class Roo.bootstrap.layout.Basic
37882 * @extends Roo.util.Observable
37883 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37884 * and does not have a titlebar, tabs or any other features. All it does is size and position
37885 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37886 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37887 * @cfg {string} region the region that it inhabits..
37888 * @cfg {bool} skipConfig skip config?
37892 Roo.bootstrap.layout.Basic = function(config){
37894 this.mgr = config.mgr;
37896 this.position = config.region;
37898 var skipConfig = config.skipConfig;
37902 * @scope Roo.BasicLayoutRegion
37906 * @event beforeremove
37907 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37908 * @param {Roo.LayoutRegion} this
37909 * @param {Roo.ContentPanel} panel The panel
37910 * @param {Object} e The cancel event object
37912 "beforeremove" : true,
37914 * @event invalidated
37915 * Fires when the layout for this region is changed.
37916 * @param {Roo.LayoutRegion} this
37918 "invalidated" : true,
37920 * @event visibilitychange
37921 * Fires when this region is shown or hidden
37922 * @param {Roo.LayoutRegion} this
37923 * @param {Boolean} visibility true or false
37925 "visibilitychange" : true,
37927 * @event paneladded
37928 * Fires when a panel is added.
37929 * @param {Roo.LayoutRegion} this
37930 * @param {Roo.ContentPanel} panel The panel
37932 "paneladded" : true,
37934 * @event panelremoved
37935 * Fires when a panel is removed.
37936 * @param {Roo.LayoutRegion} this
37937 * @param {Roo.ContentPanel} panel The panel
37939 "panelremoved" : true,
37941 * @event beforecollapse
37942 * Fires when this region before collapse.
37943 * @param {Roo.LayoutRegion} this
37945 "beforecollapse" : true,
37948 * Fires when this region is collapsed.
37949 * @param {Roo.LayoutRegion} this
37951 "collapsed" : true,
37954 * Fires when this region is expanded.
37955 * @param {Roo.LayoutRegion} this
37960 * Fires when this region is slid into view.
37961 * @param {Roo.LayoutRegion} this
37963 "slideshow" : true,
37966 * Fires when this region slides out of view.
37967 * @param {Roo.LayoutRegion} this
37969 "slidehide" : true,
37971 * @event panelactivated
37972 * Fires when a panel is activated.
37973 * @param {Roo.LayoutRegion} this
37974 * @param {Roo.ContentPanel} panel The activated panel
37976 "panelactivated" : true,
37979 * Fires when the user resizes this region.
37980 * @param {Roo.LayoutRegion} this
37981 * @param {Number} newSize The new size (width for east/west, height for north/south)
37985 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37986 this.panels = new Roo.util.MixedCollection();
37987 this.panels.getKey = this.getPanelId.createDelegate(this);
37989 this.activePanel = null;
37990 // ensure listeners are added...
37992 if (config.listeners || config.events) {
37993 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37994 listeners : config.listeners || {},
37995 events : config.events || {}
37999 if(skipConfig !== true){
38000 this.applyConfig(config);
38004 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38006 getPanelId : function(p){
38010 applyConfig : function(config){
38011 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38012 this.config = config;
38017 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38018 * the width, for horizontal (north, south) the height.
38019 * @param {Number} newSize The new width or height
38021 resizeTo : function(newSize){
38022 var el = this.el ? this.el :
38023 (this.activePanel ? this.activePanel.getEl() : null);
38025 switch(this.position){
38028 el.setWidth(newSize);
38029 this.fireEvent("resized", this, newSize);
38033 el.setHeight(newSize);
38034 this.fireEvent("resized", this, newSize);
38040 getBox : function(){
38041 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38044 getMargins : function(){
38045 return this.margins;
38048 updateBox : function(box){
38050 var el = this.activePanel.getEl();
38051 el.dom.style.left = box.x + "px";
38052 el.dom.style.top = box.y + "px";
38053 this.activePanel.setSize(box.width, box.height);
38057 * Returns the container element for this region.
38058 * @return {Roo.Element}
38060 getEl : function(){
38061 return this.activePanel;
38065 * Returns true if this region is currently visible.
38066 * @return {Boolean}
38068 isVisible : function(){
38069 return this.activePanel ? true : false;
38072 setActivePanel : function(panel){
38073 panel = this.getPanel(panel);
38074 if(this.activePanel && this.activePanel != panel){
38075 this.activePanel.setActiveState(false);
38076 this.activePanel.getEl().setLeftTop(-10000,-10000);
38078 this.activePanel = panel;
38079 panel.setActiveState(true);
38081 panel.setSize(this.box.width, this.box.height);
38083 this.fireEvent("panelactivated", this, panel);
38084 this.fireEvent("invalidated");
38088 * Show the specified panel.
38089 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38090 * @return {Roo.ContentPanel} The shown panel or null
38092 showPanel : function(panel){
38093 panel = this.getPanel(panel);
38095 this.setActivePanel(panel);
38101 * Get the active panel for this region.
38102 * @return {Roo.ContentPanel} The active panel or null
38104 getActivePanel : function(){
38105 return this.activePanel;
38109 * Add the passed ContentPanel(s)
38110 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38111 * @return {Roo.ContentPanel} The panel added (if only one was added)
38113 add : function(panel){
38114 if(arguments.length > 1){
38115 for(var i = 0, len = arguments.length; i < len; i++) {
38116 this.add(arguments[i]);
38120 if(this.hasPanel(panel)){
38121 this.showPanel(panel);
38124 var el = panel.getEl();
38125 if(el.dom.parentNode != this.mgr.el.dom){
38126 this.mgr.el.dom.appendChild(el.dom);
38128 if(panel.setRegion){
38129 panel.setRegion(this);
38131 this.panels.add(panel);
38132 el.setStyle("position", "absolute");
38133 if(!panel.background){
38134 this.setActivePanel(panel);
38135 if(this.config.initialSize && this.panels.getCount()==1){
38136 this.resizeTo(this.config.initialSize);
38139 this.fireEvent("paneladded", this, panel);
38144 * Returns true if the panel is in this region.
38145 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38146 * @return {Boolean}
38148 hasPanel : function(panel){
38149 if(typeof panel == "object"){ // must be panel obj
38150 panel = panel.getId();
38152 return this.getPanel(panel) ? true : false;
38156 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38157 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38158 * @param {Boolean} preservePanel Overrides the config preservePanel option
38159 * @return {Roo.ContentPanel} The panel that was removed
38161 remove : function(panel, preservePanel){
38162 panel = this.getPanel(panel);
38167 this.fireEvent("beforeremove", this, panel, e);
38168 if(e.cancel === true){
38171 var panelId = panel.getId();
38172 this.panels.removeKey(panelId);
38177 * Returns the panel specified or null if it's not in this region.
38178 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38179 * @return {Roo.ContentPanel}
38181 getPanel : function(id){
38182 if(typeof id == "object"){ // must be panel obj
38185 return this.panels.get(id);
38189 * Returns this regions position (north/south/east/west/center).
38192 getPosition: function(){
38193 return this.position;
38197 * Ext JS Library 1.1.1
38198 * Copyright(c) 2006-2007, Ext JS, LLC.
38200 * Originally Released Under LGPL - original licence link has changed is not relivant.
38203 * <script type="text/javascript">
38207 * @class Roo.bootstrap.layout.Region
38208 * @extends Roo.bootstrap.layout.Basic
38209 * This class represents a region in a layout manager.
38211 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38212 * @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})
38213 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38214 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38215 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38216 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38217 * @cfg {String} title The title for the region (overrides panel titles)
38218 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38219 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38220 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38221 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38222 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38223 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38224 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38225 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38226 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38227 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38229 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38230 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38231 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38232 * @cfg {Number} width For East/West panels
38233 * @cfg {Number} height For North/South panels
38234 * @cfg {Boolean} split To show the splitter
38235 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38237 * @cfg {string} cls Extra CSS classes to add to region
38239 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38240 * @cfg {string} region the region that it inhabits..
38243 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38244 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38246 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38247 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38248 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38250 Roo.bootstrap.layout.Region = function(config)
38252 this.applyConfig(config);
38254 var mgr = config.mgr;
38255 var pos = config.region;
38256 config.skipConfig = true;
38257 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38260 this.onRender(mgr.el);
38263 this.visible = true;
38264 this.collapsed = false;
38265 this.unrendered_panels = [];
38268 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38270 position: '', // set by wrapper (eg. north/south etc..)
38271 unrendered_panels : null, // unrendered panels.
38273 tabPosition : false,
38275 mgr: false, // points to 'Border'
38278 createBody : function(){
38279 /** This region's body element
38280 * @type Roo.Element */
38281 this.bodyEl = this.el.createChild({
38283 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38287 onRender: function(ctr, pos)
38289 var dh = Roo.DomHelper;
38290 /** This region's container element
38291 * @type Roo.Element */
38292 this.el = dh.append(ctr.dom, {
38294 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38296 /** This region's title element
38297 * @type Roo.Element */
38299 this.titleEl = dh.append(this.el.dom, {
38301 unselectable: "on",
38302 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38304 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38305 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38309 this.titleEl.enableDisplayMode();
38310 /** This region's title text element
38311 * @type HTMLElement */
38312 this.titleTextEl = this.titleEl.dom.firstChild;
38313 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38315 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38316 this.closeBtn.enableDisplayMode();
38317 this.closeBtn.on("click", this.closeClicked, this);
38318 this.closeBtn.hide();
38320 this.createBody(this.config);
38321 if(this.config.hideWhenEmpty){
38323 this.on("paneladded", this.validateVisibility, this);
38324 this.on("panelremoved", this.validateVisibility, this);
38326 if(this.autoScroll){
38327 this.bodyEl.setStyle("overflow", "auto");
38329 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38331 //if(c.titlebar !== false){
38332 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38333 this.titleEl.hide();
38335 this.titleEl.show();
38336 if(this.config.title){
38337 this.titleTextEl.innerHTML = this.config.title;
38341 if(this.config.collapsed){
38342 this.collapse(true);
38344 if(this.config.hidden){
38348 if (this.unrendered_panels && this.unrendered_panels.length) {
38349 for (var i =0;i< this.unrendered_panels.length; i++) {
38350 this.add(this.unrendered_panels[i]);
38352 this.unrendered_panels = null;
38358 applyConfig : function(c)
38361 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38362 var dh = Roo.DomHelper;
38363 if(c.titlebar !== false){
38364 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38365 this.collapseBtn.on("click", this.collapse, this);
38366 this.collapseBtn.enableDisplayMode();
38368 if(c.showPin === true || this.showPin){
38369 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38370 this.stickBtn.enableDisplayMode();
38371 this.stickBtn.on("click", this.expand, this);
38372 this.stickBtn.hide();
38377 /** This region's collapsed element
38378 * @type Roo.Element */
38381 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38382 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38385 if(c.floatable !== false){
38386 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38387 this.collapsedEl.on("click", this.collapseClick, this);
38390 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38391 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38392 id: "message", unselectable: "on", style:{"float":"left"}});
38393 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38395 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38396 this.expandBtn.on("click", this.expand, this);
38400 if(this.collapseBtn){
38401 this.collapseBtn.setVisible(c.collapsible == true);
38404 this.cmargins = c.cmargins || this.cmargins ||
38405 (this.position == "west" || this.position == "east" ?
38406 {top: 0, left: 2, right:2, bottom: 0} :
38407 {top: 2, left: 0, right:0, bottom: 2});
38409 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38412 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38414 this.autoScroll = c.autoScroll || false;
38419 this.duration = c.duration || .30;
38420 this.slideDuration = c.slideDuration || .45;
38425 * Returns true if this region is currently visible.
38426 * @return {Boolean}
38428 isVisible : function(){
38429 return this.visible;
38433 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38434 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38436 //setCollapsedTitle : function(title){
38437 // title = title || " ";
38438 // if(this.collapsedTitleTextEl){
38439 // this.collapsedTitleTextEl.innerHTML = title;
38443 getBox : function(){
38445 // if(!this.collapsed){
38446 b = this.el.getBox(false, true);
38448 // b = this.collapsedEl.getBox(false, true);
38453 getMargins : function(){
38454 return this.margins;
38455 //return this.collapsed ? this.cmargins : this.margins;
38458 highlight : function(){
38459 this.el.addClass("x-layout-panel-dragover");
38462 unhighlight : function(){
38463 this.el.removeClass("x-layout-panel-dragover");
38466 updateBox : function(box)
38468 if (!this.bodyEl) {
38469 return; // not rendered yet..
38473 if(!this.collapsed){
38474 this.el.dom.style.left = box.x + "px";
38475 this.el.dom.style.top = box.y + "px";
38476 this.updateBody(box.width, box.height);
38478 this.collapsedEl.dom.style.left = box.x + "px";
38479 this.collapsedEl.dom.style.top = box.y + "px";
38480 this.collapsedEl.setSize(box.width, box.height);
38483 this.tabs.autoSizeTabs();
38487 updateBody : function(w, h)
38490 this.el.setWidth(w);
38491 w -= this.el.getBorderWidth("rl");
38492 if(this.config.adjustments){
38493 w += this.config.adjustments[0];
38496 if(h !== null && h > 0){
38497 this.el.setHeight(h);
38498 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38499 h -= this.el.getBorderWidth("tb");
38500 if(this.config.adjustments){
38501 h += this.config.adjustments[1];
38503 this.bodyEl.setHeight(h);
38505 h = this.tabs.syncHeight(h);
38508 if(this.panelSize){
38509 w = w !== null ? w : this.panelSize.width;
38510 h = h !== null ? h : this.panelSize.height;
38512 if(this.activePanel){
38513 var el = this.activePanel.getEl();
38514 w = w !== null ? w : el.getWidth();
38515 h = h !== null ? h : el.getHeight();
38516 this.panelSize = {width: w, height: h};
38517 this.activePanel.setSize(w, h);
38519 if(Roo.isIE && this.tabs){
38520 this.tabs.el.repaint();
38525 * Returns the container element for this region.
38526 * @return {Roo.Element}
38528 getEl : function(){
38533 * Hides this region.
38536 //if(!this.collapsed){
38537 this.el.dom.style.left = "-2000px";
38540 // this.collapsedEl.dom.style.left = "-2000px";
38541 // this.collapsedEl.hide();
38543 this.visible = false;
38544 this.fireEvent("visibilitychange", this, false);
38548 * Shows this region if it was previously hidden.
38551 //if(!this.collapsed){
38554 // this.collapsedEl.show();
38556 this.visible = true;
38557 this.fireEvent("visibilitychange", this, true);
38560 closeClicked : function(){
38561 if(this.activePanel){
38562 this.remove(this.activePanel);
38566 collapseClick : function(e){
38568 e.stopPropagation();
38571 e.stopPropagation();
38577 * Collapses this region.
38578 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38581 collapse : function(skipAnim, skipCheck = false){
38582 if(this.collapsed) {
38586 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38588 this.collapsed = true;
38590 this.split.el.hide();
38592 if(this.config.animate && skipAnim !== true){
38593 this.fireEvent("invalidated", this);
38594 this.animateCollapse();
38596 this.el.setLocation(-20000,-20000);
38598 this.collapsedEl.show();
38599 this.fireEvent("collapsed", this);
38600 this.fireEvent("invalidated", this);
38606 animateCollapse : function(){
38611 * Expands this region if it was previously collapsed.
38612 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38613 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38616 expand : function(e, skipAnim){
38618 e.stopPropagation();
38620 if(!this.collapsed || this.el.hasActiveFx()) {
38624 this.afterSlideIn();
38627 this.collapsed = false;
38628 if(this.config.animate && skipAnim !== true){
38629 this.animateExpand();
38633 this.split.el.show();
38635 this.collapsedEl.setLocation(-2000,-2000);
38636 this.collapsedEl.hide();
38637 this.fireEvent("invalidated", this);
38638 this.fireEvent("expanded", this);
38642 animateExpand : function(){
38646 initTabs : function()
38648 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38650 var ts = new Roo.bootstrap.panel.Tabs({
38651 el: this.bodyEl.dom,
38653 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38654 disableTooltips: this.config.disableTabTips,
38655 toolbar : this.config.toolbar
38658 if(this.config.hideTabs){
38659 ts.stripWrap.setDisplayed(false);
38662 ts.resizeTabs = this.config.resizeTabs === true;
38663 ts.minTabWidth = this.config.minTabWidth || 40;
38664 ts.maxTabWidth = this.config.maxTabWidth || 250;
38665 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38666 ts.monitorResize = false;
38667 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38668 ts.bodyEl.addClass('roo-layout-tabs-body');
38669 this.panels.each(this.initPanelAsTab, this);
38672 initPanelAsTab : function(panel){
38673 var ti = this.tabs.addTab(
38677 this.config.closeOnTab && panel.isClosable(),
38680 if(panel.tabTip !== undefined){
38681 ti.setTooltip(panel.tabTip);
38683 ti.on("activate", function(){
38684 this.setActivePanel(panel);
38687 if(this.config.closeOnTab){
38688 ti.on("beforeclose", function(t, e){
38690 this.remove(panel);
38694 panel.tabItem = ti;
38699 updatePanelTitle : function(panel, title)
38701 if(this.activePanel == panel){
38702 this.updateTitle(title);
38705 var ti = this.tabs.getTab(panel.getEl().id);
38707 if(panel.tabTip !== undefined){
38708 ti.setTooltip(panel.tabTip);
38713 updateTitle : function(title){
38714 if(this.titleTextEl && !this.config.title){
38715 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38719 setActivePanel : function(panel)
38721 panel = this.getPanel(panel);
38722 if(this.activePanel && this.activePanel != panel){
38723 if(this.activePanel.setActiveState(false) === false){
38727 this.activePanel = panel;
38728 panel.setActiveState(true);
38729 if(this.panelSize){
38730 panel.setSize(this.panelSize.width, this.panelSize.height);
38733 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38735 this.updateTitle(panel.getTitle());
38737 this.fireEvent("invalidated", this);
38739 this.fireEvent("panelactivated", this, panel);
38743 * Shows the specified panel.
38744 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38745 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38747 showPanel : function(panel)
38749 panel = this.getPanel(panel);
38752 var tab = this.tabs.getTab(panel.getEl().id);
38753 if(tab.isHidden()){
38754 this.tabs.unhideTab(tab.id);
38758 this.setActivePanel(panel);
38765 * Get the active panel for this region.
38766 * @return {Roo.ContentPanel} The active panel or null
38768 getActivePanel : function(){
38769 return this.activePanel;
38772 validateVisibility : function(){
38773 if(this.panels.getCount() < 1){
38774 this.updateTitle(" ");
38775 this.closeBtn.hide();
38778 if(!this.isVisible()){
38785 * Adds the passed ContentPanel(s) to this region.
38786 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38787 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38789 add : function(panel)
38791 if(arguments.length > 1){
38792 for(var i = 0, len = arguments.length; i < len; i++) {
38793 this.add(arguments[i]);
38798 // if we have not been rendered yet, then we can not really do much of this..
38799 if (!this.bodyEl) {
38800 this.unrendered_panels.push(panel);
38807 if(this.hasPanel(panel)){
38808 this.showPanel(panel);
38811 panel.setRegion(this);
38812 this.panels.add(panel);
38813 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38814 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38815 // and hide them... ???
38816 this.bodyEl.dom.appendChild(panel.getEl().dom);
38817 if(panel.background !== true){
38818 this.setActivePanel(panel);
38820 this.fireEvent("paneladded", this, panel);
38827 this.initPanelAsTab(panel);
38831 if(panel.background !== true){
38832 this.tabs.activate(panel.getEl().id);
38834 this.fireEvent("paneladded", this, panel);
38839 * Hides the tab for the specified panel.
38840 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38842 hidePanel : function(panel){
38843 if(this.tabs && (panel = this.getPanel(panel))){
38844 this.tabs.hideTab(panel.getEl().id);
38849 * Unhides the tab for a previously hidden panel.
38850 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38852 unhidePanel : function(panel){
38853 if(this.tabs && (panel = this.getPanel(panel))){
38854 this.tabs.unhideTab(panel.getEl().id);
38858 clearPanels : function(){
38859 while(this.panels.getCount() > 0){
38860 this.remove(this.panels.first());
38865 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38866 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38867 * @param {Boolean} preservePanel Overrides the config preservePanel option
38868 * @return {Roo.ContentPanel} The panel that was removed
38870 remove : function(panel, preservePanel)
38872 panel = this.getPanel(panel);
38877 this.fireEvent("beforeremove", this, panel, e);
38878 if(e.cancel === true){
38881 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38882 var panelId = panel.getId();
38883 this.panels.removeKey(panelId);
38885 document.body.appendChild(panel.getEl().dom);
38888 this.tabs.removeTab(panel.getEl().id);
38889 }else if (!preservePanel){
38890 this.bodyEl.dom.removeChild(panel.getEl().dom);
38892 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38893 var p = this.panels.first();
38894 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38895 tempEl.appendChild(p.getEl().dom);
38896 this.bodyEl.update("");
38897 this.bodyEl.dom.appendChild(p.getEl().dom);
38899 this.updateTitle(p.getTitle());
38901 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38902 this.setActivePanel(p);
38904 panel.setRegion(null);
38905 if(this.activePanel == panel){
38906 this.activePanel = null;
38908 if(this.config.autoDestroy !== false && preservePanel !== true){
38909 try{panel.destroy();}catch(e){}
38911 this.fireEvent("panelremoved", this, panel);
38916 * Returns the TabPanel component used by this region
38917 * @return {Roo.TabPanel}
38919 getTabs : function(){
38923 createTool : function(parentEl, className){
38924 var btn = Roo.DomHelper.append(parentEl, {
38926 cls: "x-layout-tools-button",
38929 cls: "roo-layout-tools-button-inner " + className,
38933 btn.addClassOnOver("roo-layout-tools-button-over");
38938 * Ext JS Library 1.1.1
38939 * Copyright(c) 2006-2007, Ext JS, LLC.
38941 * Originally Released Under LGPL - original licence link has changed is not relivant.
38944 * <script type="text/javascript">
38950 * @class Roo.SplitLayoutRegion
38951 * @extends Roo.LayoutRegion
38952 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38954 Roo.bootstrap.layout.Split = function(config){
38955 this.cursor = config.cursor;
38956 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38959 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38961 splitTip : "Drag to resize.",
38962 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38963 useSplitTips : false,
38965 applyConfig : function(config){
38966 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38969 onRender : function(ctr,pos) {
38971 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38972 if(!this.config.split){
38977 var splitEl = Roo.DomHelper.append(ctr.dom, {
38979 id: this.el.id + "-split",
38980 cls: "roo-layout-split roo-layout-split-"+this.position,
38983 /** The SplitBar for this region
38984 * @type Roo.SplitBar */
38985 // does not exist yet...
38986 Roo.log([this.position, this.orientation]);
38988 this.split = new Roo.bootstrap.SplitBar({
38989 dragElement : splitEl,
38990 resizingElement: this.el,
38991 orientation : this.orientation
38994 this.split.on("moved", this.onSplitMove, this);
38995 this.split.useShim = this.config.useShim === true;
38996 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38997 if(this.useSplitTips){
38998 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39000 //if(config.collapsible){
39001 // this.split.el.on("dblclick", this.collapse, this);
39004 if(typeof this.config.minSize != "undefined"){
39005 this.split.minSize = this.config.minSize;
39007 if(typeof this.config.maxSize != "undefined"){
39008 this.split.maxSize = this.config.maxSize;
39010 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39011 this.hideSplitter();
39016 getHMaxSize : function(){
39017 var cmax = this.config.maxSize || 10000;
39018 var center = this.mgr.getRegion("center");
39019 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39022 getVMaxSize : function(){
39023 var cmax = this.config.maxSize || 10000;
39024 var center = this.mgr.getRegion("center");
39025 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39028 onSplitMove : function(split, newSize){
39029 this.fireEvent("resized", this, newSize);
39033 * Returns the {@link Roo.SplitBar} for this region.
39034 * @return {Roo.SplitBar}
39036 getSplitBar : function(){
39041 this.hideSplitter();
39042 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39045 hideSplitter : function(){
39047 this.split.el.setLocation(-2000,-2000);
39048 this.split.el.hide();
39054 this.split.el.show();
39056 Roo.bootstrap.layout.Split.superclass.show.call(this);
39059 beforeSlide: function(){
39060 if(Roo.isGecko){// firefox overflow auto bug workaround
39061 this.bodyEl.clip();
39063 this.tabs.bodyEl.clip();
39065 if(this.activePanel){
39066 this.activePanel.getEl().clip();
39068 if(this.activePanel.beforeSlide){
39069 this.activePanel.beforeSlide();
39075 afterSlide : function(){
39076 if(Roo.isGecko){// firefox overflow auto bug workaround
39077 this.bodyEl.unclip();
39079 this.tabs.bodyEl.unclip();
39081 if(this.activePanel){
39082 this.activePanel.getEl().unclip();
39083 if(this.activePanel.afterSlide){
39084 this.activePanel.afterSlide();
39090 initAutoHide : function(){
39091 if(this.autoHide !== false){
39092 if(!this.autoHideHd){
39093 var st = new Roo.util.DelayedTask(this.slideIn, this);
39094 this.autoHideHd = {
39095 "mouseout": function(e){
39096 if(!e.within(this.el, true)){
39100 "mouseover" : function(e){
39106 this.el.on(this.autoHideHd);
39110 clearAutoHide : function(){
39111 if(this.autoHide !== false){
39112 this.el.un("mouseout", this.autoHideHd.mouseout);
39113 this.el.un("mouseover", this.autoHideHd.mouseover);
39117 clearMonitor : function(){
39118 Roo.get(document).un("click", this.slideInIf, this);
39121 // these names are backwards but not changed for compat
39122 slideOut : function(){
39123 if(this.isSlid || this.el.hasActiveFx()){
39126 this.isSlid = true;
39127 if(this.collapseBtn){
39128 this.collapseBtn.hide();
39130 this.closeBtnState = this.closeBtn.getStyle('display');
39131 this.closeBtn.hide();
39133 this.stickBtn.show();
39136 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39137 this.beforeSlide();
39138 this.el.setStyle("z-index", 10001);
39139 this.el.slideIn(this.getSlideAnchor(), {
39140 callback: function(){
39142 this.initAutoHide();
39143 Roo.get(document).on("click", this.slideInIf, this);
39144 this.fireEvent("slideshow", this);
39151 afterSlideIn : function(){
39152 this.clearAutoHide();
39153 this.isSlid = false;
39154 this.clearMonitor();
39155 this.el.setStyle("z-index", "");
39156 if(this.collapseBtn){
39157 this.collapseBtn.show();
39159 this.closeBtn.setStyle('display', this.closeBtnState);
39161 this.stickBtn.hide();
39163 this.fireEvent("slidehide", this);
39166 slideIn : function(cb){
39167 if(!this.isSlid || this.el.hasActiveFx()){
39171 this.isSlid = false;
39172 this.beforeSlide();
39173 this.el.slideOut(this.getSlideAnchor(), {
39174 callback: function(){
39175 this.el.setLeftTop(-10000, -10000);
39177 this.afterSlideIn();
39185 slideInIf : function(e){
39186 if(!e.within(this.el)){
39191 animateCollapse : function(){
39192 this.beforeSlide();
39193 this.el.setStyle("z-index", 20000);
39194 var anchor = this.getSlideAnchor();
39195 this.el.slideOut(anchor, {
39196 callback : function(){
39197 this.el.setStyle("z-index", "");
39198 this.collapsedEl.slideIn(anchor, {duration:.3});
39200 this.el.setLocation(-10000,-10000);
39202 this.fireEvent("collapsed", this);
39209 animateExpand : function(){
39210 this.beforeSlide();
39211 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39212 this.el.setStyle("z-index", 20000);
39213 this.collapsedEl.hide({
39216 this.el.slideIn(this.getSlideAnchor(), {
39217 callback : function(){
39218 this.el.setStyle("z-index", "");
39221 this.split.el.show();
39223 this.fireEvent("invalidated", this);
39224 this.fireEvent("expanded", this);
39252 getAnchor : function(){
39253 return this.anchors[this.position];
39256 getCollapseAnchor : function(){
39257 return this.canchors[this.position];
39260 getSlideAnchor : function(){
39261 return this.sanchors[this.position];
39264 getAlignAdj : function(){
39265 var cm = this.cmargins;
39266 switch(this.position){
39282 getExpandAdj : function(){
39283 var c = this.collapsedEl, cm = this.cmargins;
39284 switch(this.position){
39286 return [-(cm.right+c.getWidth()+cm.left), 0];
39289 return [cm.right+c.getWidth()+cm.left, 0];
39292 return [0, -(cm.top+cm.bottom+c.getHeight())];
39295 return [0, cm.top+cm.bottom+c.getHeight()];
39301 * Ext JS Library 1.1.1
39302 * Copyright(c) 2006-2007, Ext JS, LLC.
39304 * Originally Released Under LGPL - original licence link has changed is not relivant.
39307 * <script type="text/javascript">
39310 * These classes are private internal classes
39312 Roo.bootstrap.layout.Center = function(config){
39313 config.region = "center";
39314 Roo.bootstrap.layout.Region.call(this, config);
39315 this.visible = true;
39316 this.minWidth = config.minWidth || 20;
39317 this.minHeight = config.minHeight || 20;
39320 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39322 // center panel can't be hidden
39326 // center panel can't be hidden
39329 getMinWidth: function(){
39330 return this.minWidth;
39333 getMinHeight: function(){
39334 return this.minHeight;
39348 Roo.bootstrap.layout.North = function(config)
39350 config.region = 'north';
39351 config.cursor = 'n-resize';
39353 Roo.bootstrap.layout.Split.call(this, config);
39357 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39358 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39359 this.split.el.addClass("roo-layout-split-v");
39361 //var size = config.initialSize || config.height;
39362 //if(this.el && typeof size != "undefined"){
39363 // this.el.setHeight(size);
39366 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39368 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39371 onRender : function(ctr, pos)
39373 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39374 var size = this.config.initialSize || this.config.height;
39375 if(this.el && typeof size != "undefined"){
39376 this.el.setHeight(size);
39381 getBox : function(){
39382 if(this.collapsed){
39383 return this.collapsedEl.getBox();
39385 var box = this.el.getBox();
39387 box.height += this.split.el.getHeight();
39392 updateBox : function(box){
39393 if(this.split && !this.collapsed){
39394 box.height -= this.split.el.getHeight();
39395 this.split.el.setLeft(box.x);
39396 this.split.el.setTop(box.y+box.height);
39397 this.split.el.setWidth(box.width);
39399 if(this.collapsed){
39400 this.updateBody(box.width, null);
39402 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39410 Roo.bootstrap.layout.South = function(config){
39411 config.region = 'south';
39412 config.cursor = 's-resize';
39413 Roo.bootstrap.layout.Split.call(this, config);
39415 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39416 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39417 this.split.el.addClass("roo-layout-split-v");
39422 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39423 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39425 onRender : function(ctr, pos)
39427 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39428 var size = this.config.initialSize || this.config.height;
39429 if(this.el && typeof size != "undefined"){
39430 this.el.setHeight(size);
39435 getBox : function(){
39436 if(this.collapsed){
39437 return this.collapsedEl.getBox();
39439 var box = this.el.getBox();
39441 var sh = this.split.el.getHeight();
39448 updateBox : function(box){
39449 if(this.split && !this.collapsed){
39450 var sh = this.split.el.getHeight();
39453 this.split.el.setLeft(box.x);
39454 this.split.el.setTop(box.y-sh);
39455 this.split.el.setWidth(box.width);
39457 if(this.collapsed){
39458 this.updateBody(box.width, null);
39460 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39464 Roo.bootstrap.layout.East = function(config){
39465 config.region = "east";
39466 config.cursor = "e-resize";
39467 Roo.bootstrap.layout.Split.call(this, config);
39469 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39470 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39471 this.split.el.addClass("roo-layout-split-h");
39475 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39476 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39478 onRender : function(ctr, pos)
39480 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39481 var size = this.config.initialSize || this.config.width;
39482 if(this.el && typeof size != "undefined"){
39483 this.el.setWidth(size);
39488 getBox : function(){
39489 if(this.collapsed){
39490 return this.collapsedEl.getBox();
39492 var box = this.el.getBox();
39494 var sw = this.split.el.getWidth();
39501 updateBox : function(box){
39502 if(this.split && !this.collapsed){
39503 var sw = this.split.el.getWidth();
39505 this.split.el.setLeft(box.x);
39506 this.split.el.setTop(box.y);
39507 this.split.el.setHeight(box.height);
39510 if(this.collapsed){
39511 this.updateBody(null, box.height);
39513 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39517 Roo.bootstrap.layout.West = function(config){
39518 config.region = "west";
39519 config.cursor = "w-resize";
39521 Roo.bootstrap.layout.Split.call(this, config);
39523 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39524 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39525 this.split.el.addClass("roo-layout-split-h");
39529 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39530 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39532 onRender: function(ctr, pos)
39534 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39535 var size = this.config.initialSize || this.config.width;
39536 if(typeof size != "undefined"){
39537 this.el.setWidth(size);
39541 getBox : function(){
39542 if(this.collapsed){
39543 return this.collapsedEl.getBox();
39545 var box = this.el.getBox();
39546 if (box.width == 0) {
39547 box.width = this.config.width; // kludge?
39550 box.width += this.split.el.getWidth();
39555 updateBox : function(box){
39556 if(this.split && !this.collapsed){
39557 var sw = this.split.el.getWidth();
39559 this.split.el.setLeft(box.x+box.width);
39560 this.split.el.setTop(box.y);
39561 this.split.el.setHeight(box.height);
39563 if(this.collapsed){
39564 this.updateBody(null, box.height);
39566 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39568 });Roo.namespace("Roo.bootstrap.panel");/*
39570 * Ext JS Library 1.1.1
39571 * Copyright(c) 2006-2007, Ext JS, LLC.
39573 * Originally Released Under LGPL - original licence link has changed is not relivant.
39576 * <script type="text/javascript">
39579 * @class Roo.ContentPanel
39580 * @extends Roo.util.Observable
39581 * A basic ContentPanel element.
39582 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39583 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39584 * @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
39585 * @cfg {Boolean} closable True if the panel can be closed/removed
39586 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39587 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39588 * @cfg {Toolbar} toolbar A toolbar for this panel
39589 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39590 * @cfg {String} title The title for this panel
39591 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39592 * @cfg {String} url Calls {@link #setUrl} with this value
39593 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39594 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39595 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39596 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39597 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39598 * @cfg {Boolean} badges render the badges
39599 * @cfg {String} cls extra classes to use
39600 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39603 * Create a new ContentPanel.
39604 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39605 * @param {String/Object} config A string to set only the title or a config object
39606 * @param {String} content (optional) Set the HTML content for this panel
39607 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39609 Roo.bootstrap.panel.Content = function( config){
39611 this.tpl = config.tpl || false;
39613 var el = config.el;
39614 var content = config.content;
39616 if(config.autoCreate){ // xtype is available if this is called from factory
39619 this.el = Roo.get(el);
39620 if(!this.el && config && config.autoCreate){
39621 if(typeof config.autoCreate == "object"){
39622 if(!config.autoCreate.id){
39623 config.autoCreate.id = config.id||el;
39625 this.el = Roo.DomHelper.append(document.body,
39626 config.autoCreate, true);
39630 cls: (config.cls || '') +
39631 (config.background ? ' bg-' + config.background : '') +
39632 " roo-layout-inactive-content",
39635 if (config.iframe) {
39639 style : 'border: 0px',
39640 src : 'about:blank'
39646 elcfg.html = config.html;
39650 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39651 if (config.iframe) {
39652 this.iframeEl = this.el.select('iframe',true).first();
39657 this.closable = false;
39658 this.loaded = false;
39659 this.active = false;
39662 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39664 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39666 this.wrapEl = this.el; //this.el.wrap();
39668 if (config.toolbar.items) {
39669 ti = config.toolbar.items ;
39670 delete config.toolbar.items ;
39674 this.toolbar.render(this.wrapEl, 'before');
39675 for(var i =0;i < ti.length;i++) {
39676 // Roo.log(['add child', items[i]]);
39677 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39679 this.toolbar.items = nitems;
39680 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39681 delete config.toolbar;
39685 // xtype created footer. - not sure if will work as we normally have to render first..
39686 if (this.footer && !this.footer.el && this.footer.xtype) {
39687 if (!this.wrapEl) {
39688 this.wrapEl = this.el.wrap();
39691 this.footer.container = this.wrapEl.createChild();
39693 this.footer = Roo.factory(this.footer, Roo);
39698 if(typeof config == "string"){
39699 this.title = config;
39701 Roo.apply(this, config);
39705 this.resizeEl = Roo.get(this.resizeEl, true);
39707 this.resizeEl = this.el;
39709 // handle view.xtype
39717 * Fires when this panel is activated.
39718 * @param {Roo.ContentPanel} this
39722 * @event deactivate
39723 * Fires when this panel is activated.
39724 * @param {Roo.ContentPanel} this
39726 "deactivate" : true,
39730 * Fires when this panel is resized if fitToFrame is true.
39731 * @param {Roo.ContentPanel} this
39732 * @param {Number} width The width after any component adjustments
39733 * @param {Number} height The height after any component adjustments
39739 * Fires when this tab is created
39740 * @param {Roo.ContentPanel} this
39751 if(this.autoScroll && !this.iframe){
39752 this.resizeEl.setStyle("overflow", "auto");
39754 // fix randome scrolling
39755 //this.el.on('scroll', function() {
39756 // Roo.log('fix random scolling');
39757 // this.scrollTo('top',0);
39760 content = content || this.content;
39762 this.setContent(content);
39764 if(config && config.url){
39765 this.setUrl(this.url, this.params, this.loadOnce);
39770 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39772 if (this.view && typeof(this.view.xtype) != 'undefined') {
39773 this.view.el = this.el.appendChild(document.createElement("div"));
39774 this.view = Roo.factory(this.view);
39775 this.view.render && this.view.render(false, '');
39779 this.fireEvent('render', this);
39782 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39792 setRegion : function(region){
39793 this.region = region;
39794 this.setActiveClass(region && !this.background);
39798 setActiveClass: function(state)
39801 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39802 this.el.setStyle('position','relative');
39804 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39805 this.el.setStyle('position', 'absolute');
39810 * Returns the toolbar for this Panel if one was configured.
39811 * @return {Roo.Toolbar}
39813 getToolbar : function(){
39814 return this.toolbar;
39817 setActiveState : function(active)
39819 this.active = active;
39820 this.setActiveClass(active);
39822 if(this.fireEvent("deactivate", this) === false){
39827 this.fireEvent("activate", this);
39831 * Updates this panel's element (not for iframe)
39832 * @param {String} content The new content
39833 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39835 setContent : function(content, loadScripts){
39840 this.el.update(content, loadScripts);
39843 ignoreResize : function(w, h){
39844 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39847 this.lastSize = {width: w, height: h};
39852 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39853 * @return {Roo.UpdateManager} The UpdateManager
39855 getUpdateManager : function(){
39859 return this.el.getUpdateManager();
39862 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39863 * Does not work with IFRAME contents
39864 * @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:
39867 url: "your-url.php",
39868 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39869 callback: yourFunction,
39870 scope: yourObject, //(optional scope)
39873 text: "Loading...",
39879 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39880 * 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.
39881 * @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}
39882 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39883 * @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.
39884 * @return {Roo.ContentPanel} this
39892 var um = this.el.getUpdateManager();
39893 um.update.apply(um, arguments);
39899 * 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.
39900 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39901 * @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)
39902 * @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)
39903 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39905 setUrl : function(url, params, loadOnce){
39907 this.iframeEl.dom.src = url;
39911 if(this.refreshDelegate){
39912 this.removeListener("activate", this.refreshDelegate);
39914 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39915 this.on("activate", this.refreshDelegate);
39916 return this.el.getUpdateManager();
39919 _handleRefresh : function(url, params, loadOnce){
39920 if(!loadOnce || !this.loaded){
39921 var updater = this.el.getUpdateManager();
39922 updater.update(url, params, this._setLoaded.createDelegate(this));
39926 _setLoaded : function(){
39927 this.loaded = true;
39931 * Returns this panel's id
39934 getId : function(){
39939 * Returns this panel's element - used by regiosn to add.
39940 * @return {Roo.Element}
39942 getEl : function(){
39943 return this.wrapEl || this.el;
39948 adjustForComponents : function(width, height)
39950 //Roo.log('adjustForComponents ');
39951 if(this.resizeEl != this.el){
39952 width -= this.el.getFrameWidth('lr');
39953 height -= this.el.getFrameWidth('tb');
39956 var te = this.toolbar.getEl();
39957 te.setWidth(width);
39958 height -= te.getHeight();
39961 var te = this.footer.getEl();
39962 te.setWidth(width);
39963 height -= te.getHeight();
39967 if(this.adjustments){
39968 width += this.adjustments[0];
39969 height += this.adjustments[1];
39971 return {"width": width, "height": height};
39974 setSize : function(width, height){
39975 if(this.fitToFrame && !this.ignoreResize(width, height)){
39976 if(this.fitContainer && this.resizeEl != this.el){
39977 this.el.setSize(width, height);
39979 var size = this.adjustForComponents(width, height);
39981 this.iframeEl.setSize(width,height);
39984 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39985 this.fireEvent('resize', this, size.width, size.height);
39992 * Returns this panel's title
39995 getTitle : function(){
39997 if (typeof(this.title) != 'object') {
40002 for (var k in this.title) {
40003 if (!this.title.hasOwnProperty(k)) {
40007 if (k.indexOf('-') >= 0) {
40008 var s = k.split('-');
40009 for (var i = 0; i<s.length; i++) {
40010 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40013 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40020 * Set this panel's title
40021 * @param {String} title
40023 setTitle : function(title){
40024 this.title = title;
40026 this.region.updatePanelTitle(this, title);
40031 * Returns true is this panel was configured to be closable
40032 * @return {Boolean}
40034 isClosable : function(){
40035 return this.closable;
40038 beforeSlide : function(){
40040 this.resizeEl.clip();
40043 afterSlide : function(){
40045 this.resizeEl.unclip();
40049 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40050 * Will fail silently if the {@link #setUrl} method has not been called.
40051 * This does not activate the panel, just updates its content.
40053 refresh : function(){
40054 if(this.refreshDelegate){
40055 this.loaded = false;
40056 this.refreshDelegate();
40061 * Destroys this panel
40063 destroy : function(){
40064 this.el.removeAllListeners();
40065 var tempEl = document.createElement("span");
40066 tempEl.appendChild(this.el.dom);
40067 tempEl.innerHTML = "";
40073 * form - if the content panel contains a form - this is a reference to it.
40074 * @type {Roo.form.Form}
40078 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40079 * This contains a reference to it.
40085 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40095 * @param {Object} cfg Xtype definition of item to add.
40099 getChildContainer: function () {
40100 return this.getEl();
40105 var ret = new Roo.factory(cfg);
40110 if (cfg.xtype.match(/^Form$/)) {
40113 //if (this.footer) {
40114 // el = this.footer.container.insertSibling(false, 'before');
40116 el = this.el.createChild();
40119 this.form = new Roo.form.Form(cfg);
40122 if ( this.form.allItems.length) {
40123 this.form.render(el.dom);
40127 // should only have one of theses..
40128 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40129 // views.. should not be just added - used named prop 'view''
40131 cfg.el = this.el.appendChild(document.createElement("div"));
40134 var ret = new Roo.factory(cfg);
40136 ret.render && ret.render(false, ''); // render blank..
40146 * @class Roo.bootstrap.panel.Grid
40147 * @extends Roo.bootstrap.panel.Content
40149 * Create a new GridPanel.
40150 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40151 * @param {Object} config A the config object
40157 Roo.bootstrap.panel.Grid = function(config)
40161 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40162 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40164 config.el = this.wrapper;
40165 //this.el = this.wrapper;
40167 if (config.container) {
40168 // ctor'ed from a Border/panel.grid
40171 this.wrapper.setStyle("overflow", "hidden");
40172 this.wrapper.addClass('roo-grid-container');
40177 if(config.toolbar){
40178 var tool_el = this.wrapper.createChild();
40179 this.toolbar = Roo.factory(config.toolbar);
40181 if (config.toolbar.items) {
40182 ti = config.toolbar.items ;
40183 delete config.toolbar.items ;
40187 this.toolbar.render(tool_el);
40188 for(var i =0;i < ti.length;i++) {
40189 // Roo.log(['add child', items[i]]);
40190 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40192 this.toolbar.items = nitems;
40194 delete config.toolbar;
40197 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40198 config.grid.scrollBody = true;;
40199 config.grid.monitorWindowResize = false; // turn off autosizing
40200 config.grid.autoHeight = false;
40201 config.grid.autoWidth = false;
40203 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40205 if (config.background) {
40206 // render grid on panel activation (if panel background)
40207 this.on('activate', function(gp) {
40208 if (!gp.grid.rendered) {
40209 gp.grid.render(this.wrapper);
40210 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40215 this.grid.render(this.wrapper);
40216 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40219 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40220 // ??? needed ??? config.el = this.wrapper;
40225 // xtype created footer. - not sure if will work as we normally have to render first..
40226 if (this.footer && !this.footer.el && this.footer.xtype) {
40228 var ctr = this.grid.getView().getFooterPanel(true);
40229 this.footer.dataSource = this.grid.dataSource;
40230 this.footer = Roo.factory(this.footer, Roo);
40231 this.footer.render(ctr);
40241 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40242 getId : function(){
40243 return this.grid.id;
40247 * Returns the grid for this panel
40248 * @return {Roo.bootstrap.Table}
40250 getGrid : function(){
40254 setSize : function(width, height){
40255 if(!this.ignoreResize(width, height)){
40256 var grid = this.grid;
40257 var size = this.adjustForComponents(width, height);
40258 // tfoot is not a footer?
40261 var gridel = grid.getGridEl();
40262 gridel.setSize(size.width, size.height);
40264 var tbd = grid.getGridEl().select('tbody', true).first();
40265 var thd = grid.getGridEl().select('thead',true).first();
40266 var tbf= grid.getGridEl().select('tfoot', true).first();
40269 size.height -= tbf.getHeight();
40272 size.height -= thd.getHeight();
40275 tbd.setSize(size.width, size.height );
40276 // this is for the account management tab -seems to work there.
40277 var thd = grid.getGridEl().select('thead',true).first();
40279 // tbd.setSize(size.width, size.height - thd.getHeight());
40288 beforeSlide : function(){
40289 this.grid.getView().scroller.clip();
40292 afterSlide : function(){
40293 this.grid.getView().scroller.unclip();
40296 destroy : function(){
40297 this.grid.destroy();
40299 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40304 * @class Roo.bootstrap.panel.Nest
40305 * @extends Roo.bootstrap.panel.Content
40307 * Create a new Panel, that can contain a layout.Border.
40310 * @param {Roo.BorderLayout} layout The layout for this panel
40311 * @param {String/Object} config A string to set only the title or a config object
40313 Roo.bootstrap.panel.Nest = function(config)
40315 // construct with only one argument..
40316 /* FIXME - implement nicer consturctors
40317 if (layout.layout) {
40319 layout = config.layout;
40320 delete config.layout;
40322 if (layout.xtype && !layout.getEl) {
40323 // then layout needs constructing..
40324 layout = Roo.factory(layout, Roo);
40328 config.el = config.layout.getEl();
40330 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40332 config.layout.monitorWindowResize = false; // turn off autosizing
40333 this.layout = config.layout;
40334 this.layout.getEl().addClass("roo-layout-nested-layout");
40335 this.layout.parent = this;
40342 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40344 setSize : function(width, height){
40345 if(!this.ignoreResize(width, height)){
40346 var size = this.adjustForComponents(width, height);
40347 var el = this.layout.getEl();
40348 if (size.height < 1) {
40349 el.setWidth(size.width);
40351 el.setSize(size.width, size.height);
40353 var touch = el.dom.offsetWidth;
40354 this.layout.layout();
40355 // ie requires a double layout on the first pass
40356 if(Roo.isIE && !this.initialized){
40357 this.initialized = true;
40358 this.layout.layout();
40363 // activate all subpanels if not currently active..
40365 setActiveState : function(active){
40366 this.active = active;
40367 this.setActiveClass(active);
40370 this.fireEvent("deactivate", this);
40374 this.fireEvent("activate", this);
40375 // not sure if this should happen before or after..
40376 if (!this.layout) {
40377 return; // should not happen..
40380 for (var r in this.layout.regions) {
40381 reg = this.layout.getRegion(r);
40382 if (reg.getActivePanel()) {
40383 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40384 reg.setActivePanel(reg.getActivePanel());
40387 if (!reg.panels.length) {
40390 reg.showPanel(reg.getPanel(0));
40399 * Returns the nested BorderLayout for this panel
40400 * @return {Roo.BorderLayout}
40402 getLayout : function(){
40403 return this.layout;
40407 * Adds a xtype elements to the layout of the nested panel
40411 xtype : 'ContentPanel',
40418 xtype : 'NestedLayoutPanel',
40424 items : [ ... list of content panels or nested layout panels.. ]
40428 * @param {Object} cfg Xtype definition of item to add.
40430 addxtype : function(cfg) {
40431 return this.layout.addxtype(cfg);
40436 * Ext JS Library 1.1.1
40437 * Copyright(c) 2006-2007, Ext JS, LLC.
40439 * Originally Released Under LGPL - original licence link has changed is not relivant.
40442 * <script type="text/javascript">
40445 * @class Roo.TabPanel
40446 * @extends Roo.util.Observable
40447 * A lightweight tab container.
40451 // basic tabs 1, built from existing content
40452 var tabs = new Roo.TabPanel("tabs1");
40453 tabs.addTab("script", "View Script");
40454 tabs.addTab("markup", "View Markup");
40455 tabs.activate("script");
40457 // more advanced tabs, built from javascript
40458 var jtabs = new Roo.TabPanel("jtabs");
40459 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40461 // set up the UpdateManager
40462 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40463 var updater = tab2.getUpdateManager();
40464 updater.setDefaultUrl("ajax1.htm");
40465 tab2.on('activate', updater.refresh, updater, true);
40467 // Use setUrl for Ajax loading
40468 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40469 tab3.setUrl("ajax2.htm", null, true);
40472 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40475 jtabs.activate("jtabs-1");
40478 * Create a new TabPanel.
40479 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40480 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40482 Roo.bootstrap.panel.Tabs = function(config){
40484 * The container element for this TabPanel.
40485 * @type Roo.Element
40487 this.el = Roo.get(config.el);
40490 if(typeof config == "boolean"){
40491 this.tabPosition = config ? "bottom" : "top";
40493 Roo.apply(this, config);
40497 if(this.tabPosition == "bottom"){
40498 // if tabs are at the bottom = create the body first.
40499 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40500 this.el.addClass("roo-tabs-bottom");
40502 // next create the tabs holders
40504 if (this.tabPosition == "west"){
40506 var reg = this.region; // fake it..
40508 if (!reg.mgr.parent) {
40511 reg = reg.mgr.parent.region;
40513 Roo.log("got nest?");
40515 if (reg.mgr.getRegion('west')) {
40516 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40517 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40518 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40519 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40520 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40528 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40529 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40530 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40531 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40536 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40539 // finally - if tabs are at the top, then create the body last..
40540 if(this.tabPosition != "bottom"){
40541 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40542 * @type Roo.Element
40544 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40545 this.el.addClass("roo-tabs-top");
40549 this.bodyEl.setStyle("position", "relative");
40551 this.active = null;
40552 this.activateDelegate = this.activate.createDelegate(this);
40557 * Fires when the active tab changes
40558 * @param {Roo.TabPanel} this
40559 * @param {Roo.TabPanelItem} activePanel The new active tab
40563 * @event beforetabchange
40564 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40565 * @param {Roo.TabPanel} this
40566 * @param {Object} e Set cancel to true on this object to cancel the tab change
40567 * @param {Roo.TabPanelItem} tab The tab being changed to
40569 "beforetabchange" : true
40572 Roo.EventManager.onWindowResize(this.onResize, this);
40573 this.cpad = this.el.getPadding("lr");
40574 this.hiddenCount = 0;
40577 // toolbar on the tabbar support...
40578 if (this.toolbar) {
40579 alert("no toolbar support yet");
40580 this.toolbar = false;
40582 var tcfg = this.toolbar;
40583 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40584 this.toolbar = new Roo.Toolbar(tcfg);
40585 if (Roo.isSafari) {
40586 var tbl = tcfg.container.child('table', true);
40587 tbl.setAttribute('width', '100%');
40595 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40598 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40600 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40602 tabPosition : "top",
40604 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40606 currentTabWidth : 0,
40608 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40612 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40616 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40618 preferredTabWidth : 175,
40620 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40622 resizeTabs : false,
40624 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40626 monitorResize : true,
40628 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40630 toolbar : false, // set by caller..
40632 region : false, /// set by caller
40634 disableTooltips : true, // not used yet...
40637 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40638 * @param {String} id The id of the div to use <b>or create</b>
40639 * @param {String} text The text for the tab
40640 * @param {String} content (optional) Content to put in the TabPanelItem body
40641 * @param {Boolean} closable (optional) True to create a close icon on the tab
40642 * @return {Roo.TabPanelItem} The created TabPanelItem
40644 addTab : function(id, text, content, closable, tpl)
40646 var item = new Roo.bootstrap.panel.TabItem({
40650 closable : closable,
40653 this.addTabItem(item);
40655 item.setContent(content);
40661 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40662 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40663 * @return {Roo.TabPanelItem}
40665 getTab : function(id){
40666 return this.items[id];
40670 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40671 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40673 hideTab : function(id){
40674 var t = this.items[id];
40677 this.hiddenCount++;
40678 this.autoSizeTabs();
40683 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40684 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40686 unhideTab : function(id){
40687 var t = this.items[id];
40689 t.setHidden(false);
40690 this.hiddenCount--;
40691 this.autoSizeTabs();
40696 * Adds an existing {@link Roo.TabPanelItem}.
40697 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40699 addTabItem : function(item)
40701 this.items[item.id] = item;
40702 this.items.push(item);
40703 this.autoSizeTabs();
40704 // if(this.resizeTabs){
40705 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40706 // this.autoSizeTabs();
40708 // item.autoSize();
40713 * Removes a {@link Roo.TabPanelItem}.
40714 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40716 removeTab : function(id){
40717 var items = this.items;
40718 var tab = items[id];
40719 if(!tab) { return; }
40720 var index = items.indexOf(tab);
40721 if(this.active == tab && items.length > 1){
40722 var newTab = this.getNextAvailable(index);
40727 this.stripEl.dom.removeChild(tab.pnode.dom);
40728 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40729 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40731 items.splice(index, 1);
40732 delete this.items[tab.id];
40733 tab.fireEvent("close", tab);
40734 tab.purgeListeners();
40735 this.autoSizeTabs();
40738 getNextAvailable : function(start){
40739 var items = this.items;
40741 // look for a next tab that will slide over to
40742 // replace the one being removed
40743 while(index < items.length){
40744 var item = items[++index];
40745 if(item && !item.isHidden()){
40749 // if one isn't found select the previous tab (on the left)
40752 var item = items[--index];
40753 if(item && !item.isHidden()){
40761 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40762 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40764 disableTab : function(id){
40765 var tab = this.items[id];
40766 if(tab && this.active != tab){
40772 * Enables a {@link Roo.TabPanelItem} that is disabled.
40773 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40775 enableTab : function(id){
40776 var tab = this.items[id];
40781 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40782 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40783 * @return {Roo.TabPanelItem} The TabPanelItem.
40785 activate : function(id)
40787 //Roo.log('activite:' + id);
40789 var tab = this.items[id];
40793 if(tab == this.active || tab.disabled){
40797 this.fireEvent("beforetabchange", this, e, tab);
40798 if(e.cancel !== true && !tab.disabled){
40800 this.active.hide();
40802 this.active = this.items[id];
40803 this.active.show();
40804 this.fireEvent("tabchange", this, this.active);
40810 * Gets the active {@link Roo.TabPanelItem}.
40811 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40813 getActiveTab : function(){
40814 return this.active;
40818 * Updates the tab body element to fit the height of the container element
40819 * for overflow scrolling
40820 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40822 syncHeight : function(targetHeight){
40823 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40824 var bm = this.bodyEl.getMargins();
40825 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40826 this.bodyEl.setHeight(newHeight);
40830 onResize : function(){
40831 if(this.monitorResize){
40832 this.autoSizeTabs();
40837 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40839 beginUpdate : function(){
40840 this.updating = true;
40844 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40846 endUpdate : function(){
40847 this.updating = false;
40848 this.autoSizeTabs();
40852 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40854 autoSizeTabs : function()
40856 var count = this.items.length;
40857 var vcount = count - this.hiddenCount;
40860 this.stripEl.hide();
40862 this.stripEl.show();
40865 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40870 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40871 var availWidth = Math.floor(w / vcount);
40872 var b = this.stripBody;
40873 if(b.getWidth() > w){
40874 var tabs = this.items;
40875 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40876 if(availWidth < this.minTabWidth){
40877 /*if(!this.sleft){ // incomplete scrolling code
40878 this.createScrollButtons();
40881 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40884 if(this.currentTabWidth < this.preferredTabWidth){
40885 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40891 * Returns the number of tabs in this TabPanel.
40894 getCount : function(){
40895 return this.items.length;
40899 * Resizes all the tabs to the passed width
40900 * @param {Number} The new width
40902 setTabWidth : function(width){
40903 this.currentTabWidth = width;
40904 for(var i = 0, len = this.items.length; i < len; i++) {
40905 if(!this.items[i].isHidden()) {
40906 this.items[i].setWidth(width);
40912 * Destroys this TabPanel
40913 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40915 destroy : function(removeEl){
40916 Roo.EventManager.removeResizeListener(this.onResize, this);
40917 for(var i = 0, len = this.items.length; i < len; i++){
40918 this.items[i].purgeListeners();
40920 if(removeEl === true){
40921 this.el.update("");
40926 createStrip : function(container)
40928 var strip = document.createElement("nav");
40929 strip.className = Roo.bootstrap.version == 4 ?
40930 "navbar-light bg-light" :
40931 "navbar navbar-default"; //"x-tabs-wrap";
40932 container.appendChild(strip);
40936 createStripList : function(strip)
40938 // div wrapper for retard IE
40939 // returns the "tr" element.
40940 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40941 //'<div class="x-tabs-strip-wrap">'+
40942 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40943 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40944 return strip.firstChild; //.firstChild.firstChild.firstChild;
40946 createBody : function(container)
40948 var body = document.createElement("div");
40949 Roo.id(body, "tab-body");
40950 //Roo.fly(body).addClass("x-tabs-body");
40951 Roo.fly(body).addClass("tab-content");
40952 container.appendChild(body);
40955 createItemBody :function(bodyEl, id){
40956 var body = Roo.getDom(id);
40958 body = document.createElement("div");
40961 //Roo.fly(body).addClass("x-tabs-item-body");
40962 Roo.fly(body).addClass("tab-pane");
40963 bodyEl.insertBefore(body, bodyEl.firstChild);
40967 createStripElements : function(stripEl, text, closable, tpl)
40969 var td = document.createElement("li"); // was td..
40970 td.className = 'nav-item';
40972 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40975 stripEl.appendChild(td);
40977 td.className = "x-tabs-closable";
40978 if(!this.closeTpl){
40979 this.closeTpl = new Roo.Template(
40980 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40981 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40982 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40985 var el = this.closeTpl.overwrite(td, {"text": text});
40986 var close = el.getElementsByTagName("div")[0];
40987 var inner = el.getElementsByTagName("em")[0];
40988 return {"el": el, "close": close, "inner": inner};
40991 // not sure what this is..
40992 // if(!this.tabTpl){
40993 //this.tabTpl = new Roo.Template(
40994 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40995 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40997 // this.tabTpl = new Roo.Template(
40998 // '<a href="#">' +
40999 // '<span unselectable="on"' +
41000 // (this.disableTooltips ? '' : ' title="{text}"') +
41001 // ' >{text}</span></a>'
41007 var template = tpl || this.tabTpl || false;
41010 template = new Roo.Template(
41011 Roo.bootstrap.version == 4 ?
41013 '<a class="nav-link" href="#" unselectable="on"' +
41014 (this.disableTooltips ? '' : ' title="{text}"') +
41017 '<a class="nav-link" href="#">' +
41018 '<span unselectable="on"' +
41019 (this.disableTooltips ? '' : ' title="{text}"') +
41020 ' >{text}</span></a>'
41025 switch (typeof(template)) {
41029 template = new Roo.Template(template);
41035 var el = template.overwrite(td, {"text": text});
41037 var inner = el.getElementsByTagName("span")[0];
41039 return {"el": el, "inner": inner};
41047 * @class Roo.TabPanelItem
41048 * @extends Roo.util.Observable
41049 * Represents an individual item (tab plus body) in a TabPanel.
41050 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41051 * @param {String} id The id of this TabPanelItem
41052 * @param {String} text The text for the tab of this TabPanelItem
41053 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41055 Roo.bootstrap.panel.TabItem = function(config){
41057 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41058 * @type Roo.TabPanel
41060 this.tabPanel = config.panel;
41062 * The id for this TabPanelItem
41065 this.id = config.id;
41067 this.disabled = false;
41069 this.text = config.text;
41071 this.loaded = false;
41072 this.closable = config.closable;
41075 * The body element for this TabPanelItem.
41076 * @type Roo.Element
41078 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41079 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41080 this.bodyEl.setStyle("display", "block");
41081 this.bodyEl.setStyle("zoom", "1");
41082 //this.hideAction();
41084 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41086 this.el = Roo.get(els.el);
41087 this.inner = Roo.get(els.inner, true);
41088 this.textEl = Roo.bootstrap.version == 4 ?
41089 this.el : Roo.get(this.el.dom.firstChild, true);
41091 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41092 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41095 // this.el.on("mousedown", this.onTabMouseDown, this);
41096 this.el.on("click", this.onTabClick, this);
41098 if(config.closable){
41099 var c = Roo.get(els.close, true);
41100 c.dom.title = this.closeText;
41101 c.addClassOnOver("close-over");
41102 c.on("click", this.closeClick, this);
41108 * Fires when this tab becomes the active tab.
41109 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41110 * @param {Roo.TabPanelItem} this
41114 * @event beforeclose
41115 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41116 * @param {Roo.TabPanelItem} this
41117 * @param {Object} e Set cancel to true on this object to cancel the close.
41119 "beforeclose": true,
41122 * Fires when this tab is closed.
41123 * @param {Roo.TabPanelItem} this
41127 * @event deactivate
41128 * Fires when this tab is no longer the active tab.
41129 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41130 * @param {Roo.TabPanelItem} this
41132 "deactivate" : true
41134 this.hidden = false;
41136 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41139 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41141 purgeListeners : function(){
41142 Roo.util.Observable.prototype.purgeListeners.call(this);
41143 this.el.removeAllListeners();
41146 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41149 this.status_node.addClass("active");
41152 this.tabPanel.stripWrap.repaint();
41154 this.fireEvent("activate", this.tabPanel, this);
41158 * Returns true if this tab is the active tab.
41159 * @return {Boolean}
41161 isActive : function(){
41162 return this.tabPanel.getActiveTab() == this;
41166 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41169 this.status_node.removeClass("active");
41171 this.fireEvent("deactivate", this.tabPanel, this);
41174 hideAction : function(){
41175 this.bodyEl.hide();
41176 this.bodyEl.setStyle("position", "absolute");
41177 this.bodyEl.setLeft("-20000px");
41178 this.bodyEl.setTop("-20000px");
41181 showAction : function(){
41182 this.bodyEl.setStyle("position", "relative");
41183 this.bodyEl.setTop("");
41184 this.bodyEl.setLeft("");
41185 this.bodyEl.show();
41189 * Set the tooltip for the tab.
41190 * @param {String} tooltip The tab's tooltip
41192 setTooltip : function(text){
41193 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41194 this.textEl.dom.qtip = text;
41195 this.textEl.dom.removeAttribute('title');
41197 this.textEl.dom.title = text;
41201 onTabClick : function(e){
41202 e.preventDefault();
41203 this.tabPanel.activate(this.id);
41206 onTabMouseDown : function(e){
41207 e.preventDefault();
41208 this.tabPanel.activate(this.id);
41211 getWidth : function(){
41212 return this.inner.getWidth();
41215 setWidth : function(width){
41216 var iwidth = width - this.linode.getPadding("lr");
41217 this.inner.setWidth(iwidth);
41218 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41219 this.linode.setWidth(width);
41223 * Show or hide the tab
41224 * @param {Boolean} hidden True to hide or false to show.
41226 setHidden : function(hidden){
41227 this.hidden = hidden;
41228 this.linode.setStyle("display", hidden ? "none" : "");
41232 * Returns true if this tab is "hidden"
41233 * @return {Boolean}
41235 isHidden : function(){
41236 return this.hidden;
41240 * Returns the text for this tab
41243 getText : function(){
41247 autoSize : function(){
41248 //this.el.beginMeasure();
41249 this.textEl.setWidth(1);
41251 * #2804 [new] Tabs in Roojs
41252 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41254 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41255 //this.el.endMeasure();
41259 * Sets the text for the tab (Note: this also sets the tooltip text)
41260 * @param {String} text The tab's text and tooltip
41262 setText : function(text){
41264 this.textEl.update(text);
41265 this.setTooltip(text);
41266 //if(!this.tabPanel.resizeTabs){
41267 // this.autoSize();
41271 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41273 activate : function(){
41274 this.tabPanel.activate(this.id);
41278 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41280 disable : function(){
41281 if(this.tabPanel.active != this){
41282 this.disabled = true;
41283 this.status_node.addClass("disabled");
41288 * Enables this TabPanelItem if it was previously disabled.
41290 enable : function(){
41291 this.disabled = false;
41292 this.status_node.removeClass("disabled");
41296 * Sets the content for this TabPanelItem.
41297 * @param {String} content The content
41298 * @param {Boolean} loadScripts true to look for and load scripts
41300 setContent : function(content, loadScripts){
41301 this.bodyEl.update(content, loadScripts);
41305 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41306 * @return {Roo.UpdateManager} The UpdateManager
41308 getUpdateManager : function(){
41309 return this.bodyEl.getUpdateManager();
41313 * Set a URL to be used to load the content for this TabPanelItem.
41314 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41315 * @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)
41316 * @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)
41317 * @return {Roo.UpdateManager} The UpdateManager
41319 setUrl : function(url, params, loadOnce){
41320 if(this.refreshDelegate){
41321 this.un('activate', this.refreshDelegate);
41323 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41324 this.on("activate", this.refreshDelegate);
41325 return this.bodyEl.getUpdateManager();
41329 _handleRefresh : function(url, params, loadOnce){
41330 if(!loadOnce || !this.loaded){
41331 var updater = this.bodyEl.getUpdateManager();
41332 updater.update(url, params, this._setLoaded.createDelegate(this));
41337 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41338 * Will fail silently if the setUrl method has not been called.
41339 * This does not activate the panel, just updates its content.
41341 refresh : function(){
41342 if(this.refreshDelegate){
41343 this.loaded = false;
41344 this.refreshDelegate();
41349 _setLoaded : function(){
41350 this.loaded = true;
41354 closeClick : function(e){
41357 this.fireEvent("beforeclose", this, o);
41358 if(o.cancel !== true){
41359 this.tabPanel.removeTab(this.id);
41363 * The text displayed in the tooltip for the close icon.
41366 closeText : "Close this tab"
41369 * This script refer to:
41370 * Title: International Telephone Input
41371 * Author: Jack O'Connor
41372 * Code version: v12.1.12
41373 * Availability: https://github.com/jackocnr/intl-tel-input.git
41376 Roo.bootstrap.PhoneInputData = function() {
41379 "Afghanistan (افغانستان)",
41384 "Albania (Shqipëri)",
41389 "Algeria (الجزائر)",
41414 "Antigua and Barbuda",
41424 "Armenia (Հայաստան)",
41440 "Austria (Österreich)",
41445 "Azerbaijan (Azərbaycan)",
41455 "Bahrain (البحرين)",
41460 "Bangladesh (বাংলাদেশ)",
41470 "Belarus (Беларусь)",
41475 "Belgium (België)",
41505 "Bosnia and Herzegovina (Босна и Херцеговина)",
41520 "British Indian Ocean Territory",
41525 "British Virgin Islands",
41535 "Bulgaria (България)",
41545 "Burundi (Uburundi)",
41550 "Cambodia (កម្ពុជា)",
41555 "Cameroon (Cameroun)",
41564 ["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"]
41567 "Cape Verde (Kabu Verdi)",
41572 "Caribbean Netherlands",
41583 "Central African Republic (République centrafricaine)",
41603 "Christmas Island",
41609 "Cocos (Keeling) Islands",
41620 "Comoros (جزر القمر)",
41625 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41630 "Congo (Republic) (Congo-Brazzaville)",
41650 "Croatia (Hrvatska)",
41671 "Czech Republic (Česká republika)",
41676 "Denmark (Danmark)",
41691 "Dominican Republic (República Dominicana)",
41695 ["809", "829", "849"]
41713 "Equatorial Guinea (Guinea Ecuatorial)",
41733 "Falkland Islands (Islas Malvinas)",
41738 "Faroe Islands (Føroyar)",
41759 "French Guiana (Guyane française)",
41764 "French Polynesia (Polynésie française)",
41779 "Georgia (საქართველო)",
41784 "Germany (Deutschland)",
41804 "Greenland (Kalaallit Nunaat)",
41841 "Guinea-Bissau (Guiné Bissau)",
41866 "Hungary (Magyarország)",
41871 "Iceland (Ísland)",
41891 "Iraq (العراق)",
41907 "Israel (ישראל)",
41934 "Jordan (الأردن)",
41939 "Kazakhstan (Казахстан)",
41960 "Kuwait (الكويت)",
41965 "Kyrgyzstan (Кыргызстан)",
41975 "Latvia (Latvija)",
41980 "Lebanon (لبنان)",
41995 "Libya (ليبيا)",
42005 "Lithuania (Lietuva)",
42020 "Macedonia (FYROM) (Македонија)",
42025 "Madagascar (Madagasikara)",
42055 "Marshall Islands",
42065 "Mauritania (موريتانيا)",
42070 "Mauritius (Moris)",
42091 "Moldova (Republica Moldova)",
42101 "Mongolia (Монгол)",
42106 "Montenegro (Crna Gora)",
42116 "Morocco (المغرب)",
42122 "Mozambique (Moçambique)",
42127 "Myanmar (Burma) (မြန်မာ)",
42132 "Namibia (Namibië)",
42147 "Netherlands (Nederland)",
42152 "New Caledonia (Nouvelle-Calédonie)",
42187 "North Korea (조선 민주주의 인민 공화국)",
42192 "Northern Mariana Islands",
42208 "Pakistan (پاکستان)",
42218 "Palestine (فلسطين)",
42228 "Papua New Guinea",
42270 "Réunion (La Réunion)",
42276 "Romania (România)",
42292 "Saint Barthélemy",
42303 "Saint Kitts and Nevis",
42313 "Saint Martin (Saint-Martin (partie française))",
42319 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42324 "Saint Vincent and the Grenadines",
42339 "São Tomé and Príncipe (São Tomé e Príncipe)",
42344 "Saudi Arabia (المملكة العربية السعودية)",
42349 "Senegal (Sénégal)",
42379 "Slovakia (Slovensko)",
42384 "Slovenia (Slovenija)",
42394 "Somalia (Soomaaliya)",
42404 "South Korea (대한민국)",
42409 "South Sudan (جنوب السودان)",
42419 "Sri Lanka (ශ්රී ලංකාව)",
42424 "Sudan (السودان)",
42434 "Svalbard and Jan Mayen",
42445 "Sweden (Sverige)",
42450 "Switzerland (Schweiz)",
42455 "Syria (سوريا)",
42500 "Trinidad and Tobago",
42505 "Tunisia (تونس)",
42510 "Turkey (Türkiye)",
42520 "Turks and Caicos Islands",
42530 "U.S. Virgin Islands",
42540 "Ukraine (Україна)",
42545 "United Arab Emirates (الإمارات العربية المتحدة)",
42567 "Uzbekistan (Oʻzbekiston)",
42577 "Vatican City (Città del Vaticano)",
42588 "Vietnam (Việt Nam)",
42593 "Wallis and Futuna (Wallis-et-Futuna)",
42598 "Western Sahara (الصحراء الغربية)",
42604 "Yemen (اليمن)",
42628 * This script refer to:
42629 * Title: International Telephone Input
42630 * Author: Jack O'Connor
42631 * Code version: v12.1.12
42632 * Availability: https://github.com/jackocnr/intl-tel-input.git
42636 * @class Roo.bootstrap.PhoneInput
42637 * @extends Roo.bootstrap.TriggerField
42638 * An input with International dial-code selection
42640 * @cfg {String} defaultDialCode default '+852'
42641 * @cfg {Array} preferedCountries default []
42644 * Create a new PhoneInput.
42645 * @param {Object} config Configuration options
42648 Roo.bootstrap.PhoneInput = function(config) {
42649 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42652 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42654 listWidth: undefined,
42656 selectedClass: 'active',
42658 invalidClass : "has-warning",
42660 validClass: 'has-success',
42662 allowed: '0123456789',
42667 * @cfg {String} defaultDialCode The default dial code when initializing the input
42669 defaultDialCode: '+852',
42672 * @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
42674 preferedCountries: false,
42676 getAutoCreate : function()
42678 var data = Roo.bootstrap.PhoneInputData();
42679 var align = this.labelAlign || this.parentLabelAlign();
42682 this.allCountries = [];
42683 this.dialCodeMapping = [];
42685 for (var i = 0; i < data.length; i++) {
42687 this.allCountries[i] = {
42691 priority: c[3] || 0,
42692 areaCodes: c[4] || null
42694 this.dialCodeMapping[c[2]] = {
42697 priority: c[3] || 0,
42698 areaCodes: c[4] || null
42710 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42711 maxlength: this.max_length,
42712 cls : 'form-control tel-input',
42713 autocomplete: 'new-password'
42716 var hiddenInput = {
42719 cls: 'hidden-tel-input'
42723 hiddenInput.name = this.name;
42726 if (this.disabled) {
42727 input.disabled = true;
42730 var flag_container = {
42747 cls: this.hasFeedback ? 'has-feedback' : '',
42753 cls: 'dial-code-holder',
42760 cls: 'roo-select2-container input-group',
42767 if (this.fieldLabel.length) {
42770 tooltip: 'This field is required'
42776 cls: 'control-label',
42782 html: this.fieldLabel
42785 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42791 if(this.indicatorpos == 'right') {
42792 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42799 if(align == 'left') {
42807 if(this.labelWidth > 12){
42808 label.style = "width: " + this.labelWidth + 'px';
42810 if(this.labelWidth < 13 && this.labelmd == 0){
42811 this.labelmd = this.labelWidth;
42813 if(this.labellg > 0){
42814 label.cls += ' col-lg-' + this.labellg;
42815 input.cls += ' col-lg-' + (12 - this.labellg);
42817 if(this.labelmd > 0){
42818 label.cls += ' col-md-' + this.labelmd;
42819 container.cls += ' col-md-' + (12 - this.labelmd);
42821 if(this.labelsm > 0){
42822 label.cls += ' col-sm-' + this.labelsm;
42823 container.cls += ' col-sm-' + (12 - this.labelsm);
42825 if(this.labelxs > 0){
42826 label.cls += ' col-xs-' + this.labelxs;
42827 container.cls += ' col-xs-' + (12 - this.labelxs);
42837 var settings = this;
42839 ['xs','sm','md','lg'].map(function(size){
42840 if (settings[size]) {
42841 cfg.cls += ' col-' + size + '-' + settings[size];
42845 this.store = new Roo.data.Store({
42846 proxy : new Roo.data.MemoryProxy({}),
42847 reader : new Roo.data.JsonReader({
42858 'name' : 'dialCode',
42862 'name' : 'priority',
42866 'name' : 'areaCodes',
42873 if(!this.preferedCountries) {
42874 this.preferedCountries = [
42881 var p = this.preferedCountries.reverse();
42884 for (var i = 0; i < p.length; i++) {
42885 for (var j = 0; j < this.allCountries.length; j++) {
42886 if(this.allCountries[j].iso2 == p[i]) {
42887 var t = this.allCountries[j];
42888 this.allCountries.splice(j,1);
42889 this.allCountries.unshift(t);
42895 this.store.proxy.data = {
42897 data: this.allCountries
42903 initEvents : function()
42906 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42908 this.indicator = this.indicatorEl();
42909 this.flag = this.flagEl();
42910 this.dialCodeHolder = this.dialCodeHolderEl();
42912 this.trigger = this.el.select('div.flag-box',true).first();
42913 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42918 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42919 _this.list.setWidth(lw);
42922 this.list.on('mouseover', this.onViewOver, this);
42923 this.list.on('mousemove', this.onViewMove, this);
42924 this.inputEl().on("keyup", this.onKeyUp, this);
42925 this.inputEl().on("keypress", this.onKeyPress, this);
42927 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42929 this.view = new Roo.View(this.list, this.tpl, {
42930 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42933 this.view.on('click', this.onViewClick, this);
42934 this.setValue(this.defaultDialCode);
42937 onTriggerClick : function(e)
42939 Roo.log('trigger click');
42944 if(this.isExpanded()){
42946 this.hasFocus = false;
42948 this.store.load({});
42949 this.hasFocus = true;
42954 isExpanded : function()
42956 return this.list.isVisible();
42959 collapse : function()
42961 if(!this.isExpanded()){
42965 Roo.get(document).un('mousedown', this.collapseIf, this);
42966 Roo.get(document).un('mousewheel', this.collapseIf, this);
42967 this.fireEvent('collapse', this);
42971 expand : function()
42975 if(this.isExpanded() || !this.hasFocus){
42979 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42980 this.list.setWidth(lw);
42983 this.restrictHeight();
42985 Roo.get(document).on('mousedown', this.collapseIf, this);
42986 Roo.get(document).on('mousewheel', this.collapseIf, this);
42988 this.fireEvent('expand', this);
42991 restrictHeight : function()
42993 this.list.alignTo(this.inputEl(), this.listAlign);
42994 this.list.alignTo(this.inputEl(), this.listAlign);
42997 onViewOver : function(e, t)
42999 if(this.inKeyMode){
43002 var item = this.view.findItemFromChild(t);
43005 var index = this.view.indexOf(item);
43006 this.select(index, false);
43011 onViewClick : function(view, doFocus, el, e)
43013 var index = this.view.getSelectedIndexes()[0];
43015 var r = this.store.getAt(index);
43018 this.onSelect(r, index);
43020 if(doFocus !== false && !this.blockFocus){
43021 this.inputEl().focus();
43025 onViewMove : function(e, t)
43027 this.inKeyMode = false;
43030 select : function(index, scrollIntoView)
43032 this.selectedIndex = index;
43033 this.view.select(index);
43034 if(scrollIntoView !== false){
43035 var el = this.view.getNode(index);
43037 this.list.scrollChildIntoView(el, false);
43042 createList : function()
43044 this.list = Roo.get(document.body).createChild({
43046 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43047 style: 'display:none'
43050 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43053 collapseIf : function(e)
43055 var in_combo = e.within(this.el);
43056 var in_list = e.within(this.list);
43057 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43059 if (in_combo || in_list || is_list) {
43065 onSelect : function(record, index)
43067 if(this.fireEvent('beforeselect', this, record, index) !== false){
43069 this.setFlagClass(record.data.iso2);
43070 this.setDialCode(record.data.dialCode);
43071 this.hasFocus = false;
43073 this.fireEvent('select', this, record, index);
43077 flagEl : function()
43079 var flag = this.el.select('div.flag',true).first();
43086 dialCodeHolderEl : function()
43088 var d = this.el.select('input.dial-code-holder',true).first();
43095 setDialCode : function(v)
43097 this.dialCodeHolder.dom.value = '+'+v;
43100 setFlagClass : function(n)
43102 this.flag.dom.className = 'flag '+n;
43105 getValue : function()
43107 var v = this.inputEl().getValue();
43108 if(this.dialCodeHolder) {
43109 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43114 setValue : function(v)
43116 var d = this.getDialCode(v);
43118 //invalid dial code
43119 if(v.length == 0 || !d || d.length == 0) {
43121 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43122 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43128 this.setFlagClass(this.dialCodeMapping[d].iso2);
43129 this.setDialCode(d);
43130 this.inputEl().dom.value = v.replace('+'+d,'');
43131 this.hiddenEl().dom.value = this.getValue();
43136 getDialCode : function(v)
43140 if (v.length == 0) {
43141 return this.dialCodeHolder.dom.value;
43145 if (v.charAt(0) != "+") {
43148 var numericChars = "";
43149 for (var i = 1; i < v.length; i++) {
43150 var c = v.charAt(i);
43153 if (this.dialCodeMapping[numericChars]) {
43154 dialCode = v.substr(1, i);
43156 if (numericChars.length == 4) {
43166 this.setValue(this.defaultDialCode);
43170 hiddenEl : function()
43172 return this.el.select('input.hidden-tel-input',true).first();
43175 // after setting val
43176 onKeyUp : function(e){
43177 this.setValue(this.getValue());
43180 onKeyPress : function(e){
43181 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43188 * @class Roo.bootstrap.MoneyField
43189 * @extends Roo.bootstrap.ComboBox
43190 * Bootstrap MoneyField class
43193 * Create a new MoneyField.
43194 * @param {Object} config Configuration options
43197 Roo.bootstrap.MoneyField = function(config) {
43199 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43203 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43206 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43208 allowDecimals : true,
43210 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43212 decimalSeparator : ".",
43214 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43216 decimalPrecision : 0,
43218 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43220 allowNegative : true,
43222 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43226 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43228 minValue : Number.NEGATIVE_INFINITY,
43230 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43232 maxValue : Number.MAX_VALUE,
43234 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43236 minText : "The minimum value for this field is {0}",
43238 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43240 maxText : "The maximum value for this field is {0}",
43242 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43243 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43245 nanText : "{0} is not a valid number",
43247 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43251 * @cfg {String} defaults currency of the MoneyField
43252 * value should be in lkey
43254 defaultCurrency : false,
43256 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43258 thousandsDelimiter : false,
43260 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43271 getAutoCreate : function()
43273 var align = this.labelAlign || this.parentLabelAlign();
43285 cls : 'form-control roo-money-amount-input',
43286 autocomplete: 'new-password'
43289 var hiddenInput = {
43293 cls: 'hidden-number-input'
43296 if(this.max_length) {
43297 input.maxlength = this.max_length;
43301 hiddenInput.name = this.name;
43304 if (this.disabled) {
43305 input.disabled = true;
43308 var clg = 12 - this.inputlg;
43309 var cmd = 12 - this.inputmd;
43310 var csm = 12 - this.inputsm;
43311 var cxs = 12 - this.inputxs;
43315 cls : 'row roo-money-field',
43319 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43323 cls: 'roo-select2-container input-group',
43327 cls : 'form-control roo-money-currency-input',
43328 autocomplete: 'new-password',
43330 name : this.currencyName
43334 cls : 'input-group-addon',
43348 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43352 cls: this.hasFeedback ? 'has-feedback' : '',
43363 if (this.fieldLabel.length) {
43366 tooltip: 'This field is required'
43372 cls: 'control-label',
43378 html: this.fieldLabel
43381 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43387 if(this.indicatorpos == 'right') {
43388 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43395 if(align == 'left') {
43403 if(this.labelWidth > 12){
43404 label.style = "width: " + this.labelWidth + 'px';
43406 if(this.labelWidth < 13 && this.labelmd == 0){
43407 this.labelmd = this.labelWidth;
43409 if(this.labellg > 0){
43410 label.cls += ' col-lg-' + this.labellg;
43411 input.cls += ' col-lg-' + (12 - this.labellg);
43413 if(this.labelmd > 0){
43414 label.cls += ' col-md-' + this.labelmd;
43415 container.cls += ' col-md-' + (12 - this.labelmd);
43417 if(this.labelsm > 0){
43418 label.cls += ' col-sm-' + this.labelsm;
43419 container.cls += ' col-sm-' + (12 - this.labelsm);
43421 if(this.labelxs > 0){
43422 label.cls += ' col-xs-' + this.labelxs;
43423 container.cls += ' col-xs-' + (12 - this.labelxs);
43434 var settings = this;
43436 ['xs','sm','md','lg'].map(function(size){
43437 if (settings[size]) {
43438 cfg.cls += ' col-' + size + '-' + settings[size];
43445 initEvents : function()
43447 this.indicator = this.indicatorEl();
43449 this.initCurrencyEvent();
43451 this.initNumberEvent();
43454 initCurrencyEvent : function()
43457 throw "can not find store for combo";
43460 this.store = Roo.factory(this.store, Roo.data);
43461 this.store.parent = this;
43465 this.triggerEl = this.el.select('.input-group-addon', true).first();
43467 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43472 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43473 _this.list.setWidth(lw);
43476 this.list.on('mouseover', this.onViewOver, this);
43477 this.list.on('mousemove', this.onViewMove, this);
43478 this.list.on('scroll', this.onViewScroll, this);
43481 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43484 this.view = new Roo.View(this.list, this.tpl, {
43485 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43488 this.view.on('click', this.onViewClick, this);
43490 this.store.on('beforeload', this.onBeforeLoad, this);
43491 this.store.on('load', this.onLoad, this);
43492 this.store.on('loadexception', this.onLoadException, this);
43494 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43495 "up" : function(e){
43496 this.inKeyMode = true;
43500 "down" : function(e){
43501 if(!this.isExpanded()){
43502 this.onTriggerClick();
43504 this.inKeyMode = true;
43509 "enter" : function(e){
43512 if(this.fireEvent("specialkey", this, e)){
43513 this.onViewClick(false);
43519 "esc" : function(e){
43523 "tab" : function(e){
43526 if(this.fireEvent("specialkey", this, e)){
43527 this.onViewClick(false);
43535 doRelay : function(foo, bar, hname){
43536 if(hname == 'down' || this.scope.isExpanded()){
43537 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43545 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43549 initNumberEvent : function(e)
43551 this.inputEl().on("keydown" , this.fireKey, this);
43552 this.inputEl().on("focus", this.onFocus, this);
43553 this.inputEl().on("blur", this.onBlur, this);
43555 this.inputEl().relayEvent('keyup', this);
43557 if(this.indicator){
43558 this.indicator.addClass('invisible');
43561 this.originalValue = this.getValue();
43563 if(this.validationEvent == 'keyup'){
43564 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43565 this.inputEl().on('keyup', this.filterValidation, this);
43567 else if(this.validationEvent !== false){
43568 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43571 if(this.selectOnFocus){
43572 this.on("focus", this.preFocus, this);
43575 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43576 this.inputEl().on("keypress", this.filterKeys, this);
43578 this.inputEl().relayEvent('keypress', this);
43581 var allowed = "0123456789";
43583 if(this.allowDecimals){
43584 allowed += this.decimalSeparator;
43587 if(this.allowNegative){
43591 if(this.thousandsDelimiter) {
43595 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43597 var keyPress = function(e){
43599 var k = e.getKey();
43601 var c = e.getCharCode();
43604 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43605 allowed.indexOf(String.fromCharCode(c)) === -1
43611 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43615 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43620 this.inputEl().on("keypress", keyPress, this);
43624 onTriggerClick : function(e)
43631 this.loadNext = false;
43633 if(this.isExpanded()){
43638 this.hasFocus = true;
43640 if(this.triggerAction == 'all') {
43641 this.doQuery(this.allQuery, true);
43645 this.doQuery(this.getRawValue());
43648 getCurrency : function()
43650 var v = this.currencyEl().getValue();
43655 restrictHeight : function()
43657 this.list.alignTo(this.currencyEl(), this.listAlign);
43658 this.list.alignTo(this.currencyEl(), this.listAlign);
43661 onViewClick : function(view, doFocus, el, e)
43663 var index = this.view.getSelectedIndexes()[0];
43665 var r = this.store.getAt(index);
43668 this.onSelect(r, index);
43672 onSelect : function(record, index){
43674 if(this.fireEvent('beforeselect', this, record, index) !== false){
43676 this.setFromCurrencyData(index > -1 ? record.data : false);
43680 this.fireEvent('select', this, record, index);
43684 setFromCurrencyData : function(o)
43688 this.lastCurrency = o;
43690 if (this.currencyField) {
43691 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43693 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43696 this.lastSelectionText = currency;
43698 //setting default currency
43699 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43700 this.setCurrency(this.defaultCurrency);
43704 this.setCurrency(currency);
43707 setFromData : function(o)
43711 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43713 this.setFromCurrencyData(c);
43718 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43720 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43723 this.setValue(value);
43727 setCurrency : function(v)
43729 this.currencyValue = v;
43732 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43737 setValue : function(v)
43739 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43745 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43747 this.inputEl().dom.value = (v == '') ? '' :
43748 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43750 if(!this.allowZero && v === '0') {
43751 this.hiddenEl().dom.value = '';
43752 this.inputEl().dom.value = '';
43759 getRawValue : function()
43761 var v = this.inputEl().getValue();
43766 getValue : function()
43768 return this.fixPrecision(this.parseValue(this.getRawValue()));
43771 parseValue : function(value)
43773 if(this.thousandsDelimiter) {
43775 r = new RegExp(",", "g");
43776 value = value.replace(r, "");
43779 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43780 return isNaN(value) ? '' : value;
43784 fixPrecision : function(value)
43786 if(this.thousandsDelimiter) {
43788 r = new RegExp(",", "g");
43789 value = value.replace(r, "");
43792 var nan = isNaN(value);
43794 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43795 return nan ? '' : value;
43797 return parseFloat(value).toFixed(this.decimalPrecision);
43800 decimalPrecisionFcn : function(v)
43802 return Math.floor(v);
43805 validateValue : function(value)
43807 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43811 var num = this.parseValue(value);
43814 this.markInvalid(String.format(this.nanText, value));
43818 if(num < this.minValue){
43819 this.markInvalid(String.format(this.minText, this.minValue));
43823 if(num > this.maxValue){
43824 this.markInvalid(String.format(this.maxText, this.maxValue));
43831 validate : function()
43833 if(this.disabled || this.allowBlank){
43838 var currency = this.getCurrency();
43840 if(this.validateValue(this.getRawValue()) && currency.length){
43845 this.markInvalid();
43849 getName: function()
43854 beforeBlur : function()
43860 var v = this.parseValue(this.getRawValue());
43867 onBlur : function()
43871 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43872 //this.el.removeClass(this.focusClass);
43875 this.hasFocus = false;
43877 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43881 var v = this.getValue();
43883 if(String(v) !== String(this.startValue)){
43884 this.fireEvent('change', this, v, this.startValue);
43887 this.fireEvent("blur", this);
43890 inputEl : function()
43892 return this.el.select('.roo-money-amount-input', true).first();
43895 currencyEl : function()
43897 return this.el.select('.roo-money-currency-input', true).first();
43900 hiddenEl : function()
43902 return this.el.select('input.hidden-number-input',true).first();
43906 * @class Roo.bootstrap.BezierSignature
43907 * @extends Roo.bootstrap.Component
43908 * Bootstrap BezierSignature class
43909 * This script refer to:
43910 * Title: Signature Pad
43912 * Availability: https://github.com/szimek/signature_pad
43915 * Create a new BezierSignature
43916 * @param {Object} config The config object
43919 Roo.bootstrap.BezierSignature = function(config){
43920 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43926 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43933 mouse_btn_down: true,
43936 * @cfg {int} canvas height
43938 canvas_height: '200px',
43941 * @cfg {float|function} Radius of a single dot.
43946 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43951 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43956 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43961 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43966 * @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.
43968 bg_color: 'rgba(0, 0, 0, 0)',
43971 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43973 dot_color: 'black',
43976 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43978 velocity_filter_weight: 0.7,
43981 * @cfg {function} Callback when stroke begin.
43986 * @cfg {function} Callback when stroke end.
43990 getAutoCreate : function()
43992 var cls = 'roo-signature column';
43995 cls += ' ' + this.cls;
44005 for(var i = 0; i < col_sizes.length; i++) {
44006 if(this[col_sizes[i]]) {
44007 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44017 cls: 'roo-signature-body',
44021 cls: 'roo-signature-body-canvas',
44022 height: this.canvas_height,
44023 width: this.canvas_width
44030 style: 'display: none'
44038 initEvents: function()
44040 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44042 var canvas = this.canvasEl();
44044 // mouse && touch event swapping...
44045 canvas.dom.style.touchAction = 'none';
44046 canvas.dom.style.msTouchAction = 'none';
44048 this.mouse_btn_down = false;
44049 canvas.on('mousedown', this._handleMouseDown, this);
44050 canvas.on('mousemove', this._handleMouseMove, this);
44051 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44053 if (window.PointerEvent) {
44054 canvas.on('pointerdown', this._handleMouseDown, this);
44055 canvas.on('pointermove', this._handleMouseMove, this);
44056 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44059 if ('ontouchstart' in window) {
44060 canvas.on('touchstart', this._handleTouchStart, this);
44061 canvas.on('touchmove', this._handleTouchMove, this);
44062 canvas.on('touchend', this._handleTouchEnd, this);
44065 Roo.EventManager.onWindowResize(this.resize, this, true);
44067 // file input event
44068 this.fileEl().on('change', this.uploadImage, this);
44075 resize: function(){
44077 var canvas = this.canvasEl().dom;
44078 var ctx = this.canvasElCtx();
44079 var img_data = false;
44081 if(canvas.width > 0) {
44082 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44084 // setting canvas width will clean img data
44087 var style = window.getComputedStyle ?
44088 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44090 var padding_left = parseInt(style.paddingLeft) || 0;
44091 var padding_right = parseInt(style.paddingRight) || 0;
44093 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44096 ctx.putImageData(img_data, 0, 0);
44100 _handleMouseDown: function(e)
44102 if (e.browserEvent.which === 1) {
44103 this.mouse_btn_down = true;
44104 this.strokeBegin(e);
44108 _handleMouseMove: function (e)
44110 if (this.mouse_btn_down) {
44111 this.strokeMoveUpdate(e);
44115 _handleMouseUp: function (e)
44117 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44118 this.mouse_btn_down = false;
44123 _handleTouchStart: function (e) {
44125 e.preventDefault();
44126 if (e.browserEvent.targetTouches.length === 1) {
44127 // var touch = e.browserEvent.changedTouches[0];
44128 // this.strokeBegin(touch);
44130 this.strokeBegin(e); // assume e catching the correct xy...
44134 _handleTouchMove: function (e) {
44135 e.preventDefault();
44136 // var touch = event.targetTouches[0];
44137 // _this._strokeMoveUpdate(touch);
44138 this.strokeMoveUpdate(e);
44141 _handleTouchEnd: function (e) {
44142 var wasCanvasTouched = e.target === this.canvasEl().dom;
44143 if (wasCanvasTouched) {
44144 e.preventDefault();
44145 // var touch = event.changedTouches[0];
44146 // _this._strokeEnd(touch);
44151 reset: function () {
44152 this._lastPoints = [];
44153 this._lastVelocity = 0;
44154 this._lastWidth = (this.min_width + this.max_width) / 2;
44155 this.canvasElCtx().fillStyle = this.dot_color;
44158 strokeMoveUpdate: function(e)
44160 this.strokeUpdate(e);
44162 if (this.throttle) {
44163 this.throttleStroke(this.strokeUpdate, this.throttle);
44166 this.strokeUpdate(e);
44170 strokeBegin: function(e)
44172 var newPointGroup = {
44173 color: this.dot_color,
44177 if (typeof this.onBegin === 'function') {
44181 this.curve_data.push(newPointGroup);
44183 this.strokeUpdate(e);
44186 strokeUpdate: function(e)
44188 var rect = this.canvasEl().dom.getBoundingClientRect();
44189 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44190 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44191 var lastPoints = lastPointGroup.points;
44192 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44193 var isLastPointTooClose = lastPoint
44194 ? point.distanceTo(lastPoint) <= this.min_distance
44196 var color = lastPointGroup.color;
44197 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44198 var curve = this.addPoint(point);
44200 this.drawDot({color: color, point: point});
44203 this.drawCurve({color: color, curve: curve});
44213 strokeEnd: function(e)
44215 this.strokeUpdate(e);
44216 if (typeof this.onEnd === 'function') {
44221 addPoint: function (point) {
44222 var _lastPoints = this._lastPoints;
44223 _lastPoints.push(point);
44224 if (_lastPoints.length > 2) {
44225 if (_lastPoints.length === 3) {
44226 _lastPoints.unshift(_lastPoints[0]);
44228 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44229 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44230 _lastPoints.shift();
44236 calculateCurveWidths: function (startPoint, endPoint) {
44237 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44238 (1 - this.velocity_filter_weight) * this._lastVelocity;
44240 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44243 start: this._lastWidth
44246 this._lastVelocity = velocity;
44247 this._lastWidth = newWidth;
44251 drawDot: function (_a) {
44252 var color = _a.color, point = _a.point;
44253 var ctx = this.canvasElCtx();
44254 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44256 this.drawCurveSegment(point.x, point.y, width);
44258 ctx.fillStyle = color;
44262 drawCurve: function (_a) {
44263 var color = _a.color, curve = _a.curve;
44264 var ctx = this.canvasElCtx();
44265 var widthDelta = curve.endWidth - curve.startWidth;
44266 var drawSteps = Math.floor(curve.length()) * 2;
44268 ctx.fillStyle = color;
44269 for (var i = 0; i < drawSteps; i += 1) {
44270 var t = i / drawSteps;
44276 var x = uuu * curve.startPoint.x;
44277 x += 3 * uu * t * curve.control1.x;
44278 x += 3 * u * tt * curve.control2.x;
44279 x += ttt * curve.endPoint.x;
44280 var y = uuu * curve.startPoint.y;
44281 y += 3 * uu * t * curve.control1.y;
44282 y += 3 * u * tt * curve.control2.y;
44283 y += ttt * curve.endPoint.y;
44284 var width = curve.startWidth + ttt * widthDelta;
44285 this.drawCurveSegment(x, y, width);
44291 drawCurveSegment: function (x, y, width) {
44292 var ctx = this.canvasElCtx();
44294 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44295 this.is_empty = false;
44300 var ctx = this.canvasElCtx();
44301 var canvas = this.canvasEl().dom;
44302 ctx.fillStyle = this.bg_color;
44303 ctx.clearRect(0, 0, canvas.width, canvas.height);
44304 ctx.fillRect(0, 0, canvas.width, canvas.height);
44305 this.curve_data = [];
44307 this.is_empty = true;
44312 return this.el.select('input',true).first();
44315 canvasEl: function()
44317 return this.el.select('canvas',true).first();
44320 canvasElCtx: function()
44322 return this.el.select('canvas',true).first().dom.getContext('2d');
44325 getImage: function(type)
44327 if(this.is_empty) {
44332 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44335 drawFromImage: function(img_src)
44337 var img = new Image();
44339 img.onload = function(){
44340 this.canvasElCtx().drawImage(img, 0, 0);
44345 this.is_empty = false;
44348 selectImage: function()
44350 this.fileEl().dom.click();
44353 uploadImage: function(e)
44355 var reader = new FileReader();
44357 reader.onload = function(e){
44358 var img = new Image();
44359 img.onload = function(){
44361 this.canvasElCtx().drawImage(img, 0, 0);
44363 img.src = e.target.result;
44366 reader.readAsDataURL(e.target.files[0]);
44369 // Bezier Point Constructor
44370 Point: (function () {
44371 function Point(x, y, time) {
44374 this.time = time || Date.now();
44376 Point.prototype.distanceTo = function (start) {
44377 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44379 Point.prototype.equals = function (other) {
44380 return this.x === other.x && this.y === other.y && this.time === other.time;
44382 Point.prototype.velocityFrom = function (start) {
44383 return this.time !== start.time
44384 ? this.distanceTo(start) / (this.time - start.time)
44391 // Bezier Constructor
44392 Bezier: (function () {
44393 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44394 this.startPoint = startPoint;
44395 this.control2 = control2;
44396 this.control1 = control1;
44397 this.endPoint = endPoint;
44398 this.startWidth = startWidth;
44399 this.endWidth = endWidth;
44401 Bezier.fromPoints = function (points, widths, scope) {
44402 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44403 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44404 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44406 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44407 var dx1 = s1.x - s2.x;
44408 var dy1 = s1.y - s2.y;
44409 var dx2 = s2.x - s3.x;
44410 var dy2 = s2.y - s3.y;
44411 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44412 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44413 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44414 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44415 var dxm = m1.x - m2.x;
44416 var dym = m1.y - m2.y;
44417 var k = l2 / (l1 + l2);
44418 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44419 var tx = s2.x - cm.x;
44420 var ty = s2.y - cm.y;
44422 c1: new scope.Point(m1.x + tx, m1.y + ty),
44423 c2: new scope.Point(m2.x + tx, m2.y + ty)
44426 Bezier.prototype.length = function () {
44431 for (var i = 0; i <= steps; i += 1) {
44433 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44434 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44436 var xdiff = cx - px;
44437 var ydiff = cy - py;
44438 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44445 Bezier.prototype.point = function (t, start, c1, c2, end) {
44446 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44447 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44448 + (3.0 * c2 * (1.0 - t) * t * t)
44449 + (end * t * t * t);
44454 throttleStroke: function(fn, wait) {
44455 if (wait === void 0) { wait = 250; }
44457 var timeout = null;
44461 var later = function () {
44462 previous = Date.now();
44464 result = fn.apply(storedContext, storedArgs);
44466 storedContext = null;
44470 return function wrapper() {
44472 for (var _i = 0; _i < arguments.length; _i++) {
44473 args[_i] = arguments[_i];
44475 var now = Date.now();
44476 var remaining = wait - (now - previous);
44477 storedContext = this;
44479 if (remaining <= 0 || remaining > wait) {
44481 clearTimeout(timeout);
44485 result = fn.apply(storedContext, storedArgs);
44487 storedContext = null;
44491 else if (!timeout) {
44492 timeout = window.setTimeout(later, remaining);