2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2079 layoutCls : function()
2083 Roo.log(this.margin_bottom.length);
2084 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2090 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2095 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2101 // more generic support?
2109 // Roo.log("Call onRender: " + this.xtype);
2110 /* We are looking at something like this.
2112 <img src="..." class="card-img-top" alt="...">
2113 <div class="card-body">
2114 <h5 class="card-title">Card title</h5>
2115 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117 >> this bit is really the body...
2118 <div> << we will ad dthis in hopefully it will not break shit.
2120 ** card text does not actually have any styling...
2122 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2125 <a href="#" class="card-link">Card link</a>
2128 <div class="card-footer">
2129 <small class="text-muted">Last updated 3 mins ago</small>
2133 getAutoCreate : function(){
2141 if (this.weight.length && this.weight != 'light') {
2142 cfg.cls += ' text-white';
2144 cfg.cls += ' text-dark'; // need as it's nested..
2146 if (this.weight.length) {
2147 cfg.cls += ' bg-' + this.weight;
2150 cfg.cls += ' ' + this.layoutCls();
2153 var hdr_ctr = false;
2154 if (this.header.length) {
2156 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2165 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2171 if (this.collapsable) {
2174 cls : 'd-block user-select-none',
2178 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2183 hdr.cn.push(hdr_ctr);
2188 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2193 if (this.header_image.length) {
2196 cls : 'card-img-top',
2197 src: this.header_image // escape?
2202 cls : 'card-img-top d-none'
2208 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2212 if (this.collapsable || this.rotateable) {
2215 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2222 if (this.title.length) {
2226 src: this.title // escape?
2230 if (this.subtitle.length) {
2234 src: this.subtitle // escape?
2240 cls : 'roo-card-body-ctr'
2243 if (this.html.length) {
2249 // fixme ? handle objects?
2251 if (this.footer.length) {
2254 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2259 cfg.cn.push({cls : 'card-footer d-none'});
2268 getCardHeader : function()
2270 var ret = this.el.select('.card-header',true).first();
2271 if (ret.hasClass('d-none')) {
2272 ret.removeClass('d-none');
2277 getCardFooter : function()
2279 var ret = this.el.select('.card-footer',true).first();
2280 if (ret.hasClass('d-none')) {
2281 ret.removeClass('d-none');
2286 getCardImageTop : function()
2288 var ret = this.el.select('.card-img-top',true).first();
2289 if (ret.hasClass('d-none')) {
2290 ret.removeClass('d-none');
2296 getChildContainer : function()
2302 return this.el.select('.roo-card-body-ctr',true).first();
2305 initEvents: function()
2307 this.bodyEl = this.el.select('.card-body',true).first();
2308 this.containerEl = this.getChildContainer();
2310 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311 containerScroll: true,
2312 ddGroup: this.drag_group || 'default_card_drag_group'
2314 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316 if (this.dropable) {
2317 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318 containerScroll: true,
2319 ddGroup: this.drop_group || 'default_card_drag_group'
2321 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2328 if (this.collapsable) {
2329 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331 if (this.rotateable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334 this.collapsableEl = this.el.select('.roo-collapsable').first();
2336 this.footerEl = this.el.select('.card-footer').first();
2337 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339 this.headerEl = this.el.select('.card-header',true).first();
2342 this.el.addClass('roo-card-rotated');
2343 this.fireEvent('rotate', this, true);
2347 getDragData : function(e)
2349 var target = this.getEl();
2351 //this.handleSelection(e);
2356 nodes: this.getEl(),
2361 dragData.ddel = target.dom ; // the div element
2362 Roo.log(target.getWidth( ));
2363 dragData.ddel.style.width = target.getWidth() + 'px';
2370 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2371 * whole Element becomes the target, and this causes the drop gesture to append.
2373 getTargetFromEvent : function(e, dragged_card_el)
2375 var target = e.getTarget();
2376 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377 target = target.parentNode;
2388 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389 // see if target is one of the 'cards'...
2392 //Roo.log(this.items.length);
2395 var last_card_n = 0;
2397 for (var i = 0;i< this.items.length;i++) {
2399 if (!this.items[i].el.hasClass('card')) {
2402 pos = this.getDropPoint(e, this.items[i].el.dom);
2404 cards_len = ret.cards.length;
2405 //Roo.log(this.items[i].el.dom.id);
2406 ret.cards.push(this.items[i]);
2408 if (ret.card_n < 0 && pos == 'above') {
2409 ret.position = cards_len > 0 ? 'below' : pos;
2410 ret.items_n = i > 0 ? i - 1 : 0;
2411 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2412 ret.card = ret.cards[ret.card_n];
2415 if (!ret.cards.length) {
2417 ret.position = 'below';
2421 // could not find a card.. stick it at the end..
2422 if (ret.card_n < 0) {
2423 ret.card_n = last_card_n;
2424 ret.card = ret.cards[last_card_n];
2425 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426 ret.position = 'below';
2429 if (this.items[ret.items_n].el == dragged_card_el) {
2433 if (ret.position == 'below') {
2434 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2436 if (card_after && card_after.el == dragged_card_el) {
2443 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2445 if (card_before && card_before.el == dragged_card_el) {
2452 onNodeEnter : function(n, dd, e, data){
2455 onNodeOver : function(n, dd, e, data)
2458 var target_info = this.getTargetFromEvent(e,data.source.el);
2459 if (target_info === false) {
2460 this.dropPlaceHolder('hide');
2463 Roo.log(['getTargetFromEvent', target_info ]);
2466 this.dropPlaceHolder('show', target_info,data);
2470 onNodeOut : function(n, dd, e, data){
2471 this.dropPlaceHolder('hide');
2474 onNodeDrop : function(n, dd, e, data)
2477 // call drop - return false if
2479 // this could actually fail - if the Network drops..
2480 // we will ignore this at present..- client should probably reload
2481 // the whole set of cards if stuff like that fails.
2484 var info = this.getTargetFromEvent(e,data.source.el);
2485 if (info === false) {
2488 this.dropPlaceHolder('hide');
2494 this.acceptCard(data.source, info.position, info.card, info.items_n);
2498 firstChildCard : function()
2500 for (var i = 0;i< this.items.length;i++) {
2502 if (!this.items[i].el.hasClass('card')) {
2505 return this.items[i];
2507 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2512 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2514 acceptCard : function(move_card, position, next_to_card )
2516 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2522 move_card.parent().removeCard(move_card);
2525 var dom = move_card.el.dom;
2526 dom.style.width = ''; // clear with - which is set by drag.
2528 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529 var cardel = next_to_card.el.dom;
2531 if (position == 'above' ) {
2532 cardel.parentNode.insertBefore(dom, cardel);
2533 } else if (cardel.nextSibling) {
2534 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2536 cardel.parentNode.append(dom);
2539 // card container???
2540 this.containerEl.dom.append(dom);
2543 //FIXME HANDLE card = true
2545 // add this to the correct place in items.
2547 // remove Card from items.
2550 if (this.items.length) {
2552 //Roo.log([info.items_n, info.position, this.items.length]);
2553 for (var i =0; i < this.items.length; i++) {
2554 if (i == to_items_n && position == 'above') {
2555 nitems.push(move_card);
2557 nitems.push(this.items[i]);
2558 if (i == to_items_n && position == 'below') {
2559 nitems.push(move_card);
2562 this.items = nitems;
2563 Roo.log(this.items);
2565 this.items.push(move_card);
2568 move_card.parentId = this.id;
2574 removeCard : function(c)
2576 this.items = this.items.filter(function(e) { return e != c });
2579 dom.parentNode.removeChild(dom);
2580 dom.style.width = ''; // clear with - which is set by drag.
2585 /** Decide whether to drop above or below a View node. */
2586 getDropPoint : function(e, n, dd)
2591 if (n == this.containerEl.dom) {
2594 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595 var c = t + (b - t) / 2;
2596 var y = Roo.lib.Event.getPageY(e);
2603 onToggleCollapse : function(e)
2605 if (this.collapsed) {
2606 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607 this.collapsableEl.addClass('show');
2608 this.collapsed = false;
2611 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612 this.collapsableEl.removeClass('show');
2613 this.collapsed = true;
2618 onToggleRotate : function(e)
2620 this.collapsableEl.removeClass('show');
2621 this.footerEl.removeClass('d-none');
2622 this.el.removeClass('roo-card-rotated');
2623 this.el.removeClass('d-none');
2626 this.collapsableEl.addClass('show');
2627 this.rotated = false;
2628 this.fireEvent('rotate', this, this.rotated);
2631 this.el.addClass('roo-card-rotated');
2632 this.footerEl.addClass('d-none');
2633 this.el.select('.roo-collapsable').removeClass('show');
2635 this.rotated = true;
2636 this.fireEvent('rotate', this, this.rotated);
2640 dropPlaceHolder: function (action, info, data)
2642 if (this.dropEl === false) {
2643 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647 this.dropEl.removeClass(['d-none', 'd-block']);
2648 if (action == 'hide') {
2650 this.dropEl.addClass('d-none');
2653 // FIXME - info.card == true!!!
2654 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2656 if (info.card !== true) {
2657 var cardel = info.card.el.dom;
2659 if (info.position == 'above') {
2660 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661 } else if (cardel.nextSibling) {
2662 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2664 cardel.parentNode.append(this.dropEl.dom);
2667 // card container???
2668 this.containerEl.dom.append(this.dropEl.dom);
2671 this.dropEl.addClass('d-block roo-card-dropzone');
2673 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2680 setHeaderText: function(html)
2683 if (this.headerContainerEl) {
2684 this.headerContainerEl.dom.innerHTML = html;
2694 * Card header - holder for the card header elements.
2699 * @class Roo.bootstrap.CardHeader
2700 * @extends Roo.bootstrap.Element
2701 * Bootstrap CardHeader class
2703 * Create a new Card Header - that you can embed children into
2704 * @param {Object} config The config object
2707 Roo.bootstrap.CardHeader = function(config){
2708 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2714 container_method : 'getCardHeader'
2727 * Card footer - holder for the card footer elements.
2732 * @class Roo.bootstrap.CardFooter
2733 * @extends Roo.bootstrap.Element
2734 * Bootstrap CardFooter class
2736 * Create a new Card Footer - that you can embed children into
2737 * @param {Object} config The config object
2740 Roo.bootstrap.CardFooter = function(config){
2741 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2747 container_method : 'getCardFooter'
2760 * Card header - holder for the card header elements.
2765 * @class Roo.bootstrap.CardImageTop
2766 * @extends Roo.bootstrap.Element
2767 * Bootstrap CardImageTop class
2769 * Create a new Card Image Top container
2770 * @param {Object} config The config object
2773 Roo.bootstrap.CardImageTop = function(config){
2774 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2780 container_method : 'getCardImageTop'
2798 * @class Roo.bootstrap.Img
2799 * @extends Roo.bootstrap.Component
2800 * Bootstrap Img class
2801 * @cfg {Boolean} imgResponsive false | true
2802 * @cfg {String} border rounded | circle | thumbnail
2803 * @cfg {String} src image source
2804 * @cfg {String} alt image alternative text
2805 * @cfg {String} href a tag href
2806 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807 * @cfg {String} xsUrl xs image source
2808 * @cfg {String} smUrl sm image source
2809 * @cfg {String} mdUrl md image source
2810 * @cfg {String} lgUrl lg image source
2813 * Create a new Input
2814 * @param {Object} config The config object
2817 Roo.bootstrap.Img = function(config){
2818 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2824 * The img click event for the img.
2825 * @param {Roo.EventObject} e
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2833 imgResponsive: true,
2843 getAutoCreate : function()
2845 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846 return this.createSingleImg();
2851 cls: 'roo-image-responsive-group',
2856 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2858 if(!_this[size + 'Url']){
2864 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865 html: _this.html || cfg.html,
2866 src: _this[size + 'Url']
2869 img.cls += ' roo-image-responsive-' + size;
2871 var s = ['xs', 'sm', 'md', 'lg'];
2873 s.splice(s.indexOf(size), 1);
2875 Roo.each(s, function(ss){
2876 img.cls += ' hidden-' + ss;
2879 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880 cfg.cls += ' img-' + _this.border;
2884 cfg.alt = _this.alt;
2897 a.target = _this.target;
2901 cfg.cn.push((_this.href) ? a : img);
2908 createSingleImg : function()
2912 cls: (this.imgResponsive) ? 'img-responsive' : '',
2914 src : 'about:blank' // just incase src get's set to undefined?!?
2917 cfg.html = this.html || cfg.html;
2919 cfg.src = this.src || cfg.src;
2921 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922 cfg.cls += ' img-' + this.border;
2939 a.target = this.target;
2944 return (this.href) ? a : cfg;
2947 initEvents: function()
2950 this.el.on('click', this.onClick, this);
2955 onClick : function(e)
2957 Roo.log('img onclick');
2958 this.fireEvent('click', this, e);
2961 * Sets the url of the image - used to update it
2962 * @param {String} url the url of the image
2965 setSrc : function(url)
2969 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970 this.el.dom.src = url;
2974 this.el.select('img', true).first().dom.src = url;
2990 * @class Roo.bootstrap.Link
2991 * @extends Roo.bootstrap.Component
2992 * Bootstrap Link Class
2993 * @cfg {String} alt image alternative text
2994 * @cfg {String} href a tag href
2995 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996 * @cfg {String} html the content of the link.
2997 * @cfg {String} anchor name for the anchor link
2998 * @cfg {String} fa - favicon
3000 * @cfg {Boolean} preventDefault (true | false) default false
3004 * Create a new Input
3005 * @param {Object} config The config object
3008 Roo.bootstrap.Link = function(config){
3009 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3015 * The img click event for the img.
3016 * @param {Roo.EventObject} e
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3026 preventDefault: false,
3032 getAutoCreate : function()
3034 var html = this.html || '';
3036 if (this.fa !== false) {
3037 html = '<i class="fa fa-' + this.fa + '"></i>';
3042 // anchor's do not require html/href...
3043 if (this.anchor === false) {
3045 cfg.href = this.href || '#';
3047 cfg.name = this.anchor;
3048 if (this.html !== false || this.fa !== false) {
3051 if (this.href !== false) {
3052 cfg.href = this.href;
3056 if(this.alt !== false){
3061 if(this.target !== false) {
3062 cfg.target = this.target;
3068 initEvents: function() {
3070 if(!this.href || this.preventDefault){
3071 this.el.on('click', this.onClick, this);
3075 onClick : function(e)
3077 if(this.preventDefault){
3080 //Roo.log('img onclick');
3081 this.fireEvent('click', this, e);
3094 * @class Roo.bootstrap.Header
3095 * @extends Roo.bootstrap.Component
3096 * Bootstrap Header class
3097 * @cfg {String} html content of header
3098 * @cfg {Number} level (1|2|3|4|5|6) default 1
3101 * Create a new Header
3102 * @param {Object} config The config object
3106 Roo.bootstrap.Header = function(config){
3107 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3118 getAutoCreate : function(){
3123 tag: 'h' + (1 *this.level),
3124 html: this.html || ''
3136 * Ext JS Library 1.1.1
3137 * Copyright(c) 2006-2007, Ext JS, LLC.
3139 * Originally Released Under LGPL - original licence link has changed is not relivant.
3142 * <script type="text/javascript">
3146 * @class Roo.bootstrap.MenuMgr
3147 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3150 Roo.bootstrap.MenuMgr = function(){
3151 var menus, active, groups = {}, attached = false, lastShow = new Date();
3153 // private - called when first menu is created
3156 active = new Roo.util.MixedCollection();
3157 Roo.get(document).addKeyListener(27, function(){
3158 if(active.length > 0){
3166 if(active && active.length > 0){
3167 var c = active.clone();
3177 if(active.length < 1){
3178 Roo.get(document).un("mouseup", onMouseDown);
3186 var last = active.last();
3187 lastShow = new Date();
3190 Roo.get(document).on("mouseup", onMouseDown);
3195 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196 m.parentMenu.activeChild = m;
3197 }else if(last && last.isVisible()){
3198 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3203 function onBeforeHide(m){
3205 m.activeChild.hide();
3207 if(m.autoHideTimer){
3208 clearTimeout(m.autoHideTimer);
3209 delete m.autoHideTimer;
3214 function onBeforeShow(m){
3215 var pm = m.parentMenu;
3216 if(!pm && !m.allowOtherMenus){
3218 }else if(pm && pm.activeChild && active != m){
3219 pm.activeChild.hide();
3223 // private this should really trigger on mouseup..
3224 function onMouseDown(e){
3225 Roo.log("on Mouse Up");
3227 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228 Roo.log("MenuManager hideAll");
3237 function onBeforeCheck(mi, state){
3239 var g = groups[mi.group];
3240 for(var i = 0, l = g.length; i < l; i++){
3242 g[i].setChecked(false);
3251 * Hides all menus that are currently visible
3253 hideAll : function(){
3258 register : function(menu){
3262 menus[menu.id] = menu;
3263 menu.on("beforehide", onBeforeHide);
3264 menu.on("hide", onHide);
3265 menu.on("beforeshow", onBeforeShow);
3266 menu.on("show", onShow);
3268 if(g && menu.events["checkchange"]){
3272 groups[g].push(menu);
3273 menu.on("checkchange", onCheck);
3278 * Returns a {@link Roo.menu.Menu} object
3279 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280 * be used to generate and return a new Menu instance.
3282 get : function(menu){
3283 if(typeof menu == "string"){ // menu id
3285 }else if(menu.events){ // menu instance
3288 /*else if(typeof menu.length == 'number'){ // array of menu items?
3289 return new Roo.bootstrap.Menu({items:menu});
3290 }else{ // otherwise, must be a config
3291 return new Roo.bootstrap.Menu(menu);
3298 unregister : function(menu){
3299 delete menus[menu.id];
3300 menu.un("beforehide", onBeforeHide);
3301 menu.un("hide", onHide);
3302 menu.un("beforeshow", onBeforeShow);
3303 menu.un("show", onShow);
3305 if(g && menu.events["checkchange"]){
3306 groups[g].remove(menu);
3307 menu.un("checkchange", onCheck);
3312 registerCheckable : function(menuItem){
3313 var g = menuItem.group;
3318 groups[g].push(menuItem);
3319 menuItem.on("beforecheckchange", onBeforeCheck);
3324 unregisterCheckable : function(menuItem){
3325 var g = menuItem.group;
3327 groups[g].remove(menuItem);
3328 menuItem.un("beforecheckchange", onBeforeCheck);
3340 * @class Roo.bootstrap.Menu
3341 * @extends Roo.bootstrap.Component
3342 * Bootstrap Menu class - container for MenuItems
3343 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344 * @cfg {bool} hidden if the menu should be hidden when rendered.
3345 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3346 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3350 * @param {Object} config The config object
3354 Roo.bootstrap.Menu = function(config){
3355 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356 if (this.registerMenu && this.type != 'treeview') {
3357 Roo.bootstrap.MenuMgr.register(this);
3364 * Fires before this menu is displayed (return false to block)
3365 * @param {Roo.menu.Menu} this
3370 * Fires before this menu is hidden (return false to block)
3371 * @param {Roo.menu.Menu} this
3376 * Fires after this menu is displayed
3377 * @param {Roo.menu.Menu} this
3382 * Fires after this menu is hidden
3383 * @param {Roo.menu.Menu} this
3388 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389 * @param {Roo.menu.Menu} this
3390 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391 * @param {Roo.EventObject} e
3396 * Fires when the mouse is hovering over this menu
3397 * @param {Roo.menu.Menu} this
3398 * @param {Roo.EventObject} e
3399 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3404 * Fires when the mouse exits this menu
3405 * @param {Roo.menu.Menu} this
3406 * @param {Roo.EventObject} e
3407 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3412 * Fires when a menu item contained in this menu is clicked
3413 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414 * @param {Roo.EventObject} e
3418 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3425 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3428 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3430 registerMenu : true,
3432 menuItems :false, // stores the menu items..
3442 getChildContainer : function() {
3446 getAutoCreate : function(){
3448 //if (['right'].indexOf(this.align)!==-1) {
3449 // cfg.cn[1].cls += ' pull-right'
3455 cls : 'dropdown-menu' ,
3456 style : 'z-index:1000'
3460 if (this.type === 'submenu') {
3461 cfg.cls = 'submenu active';
3463 if (this.type === 'treeview') {
3464 cfg.cls = 'treeview-menu';
3469 initEvents : function() {
3471 // Roo.log("ADD event");
3472 // Roo.log(this.triggerEl.dom);
3474 this.triggerEl.on('click', this.onTriggerClick, this);
3476 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3479 if (this.triggerEl.hasClass('nav-item')) {
3480 // dropdown toggle on the 'a' in BS4?
3481 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3483 this.triggerEl.addClass('dropdown-toggle');
3486 this.el.on('touchstart' , this.onTouch, this);
3488 this.el.on('click' , this.onClick, this);
3490 this.el.on("mouseover", this.onMouseOver, this);
3491 this.el.on("mouseout", this.onMouseOut, this);
3495 findTargetItem : function(e)
3497 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3501 //Roo.log(t); Roo.log(t.id);
3503 //Roo.log(this.menuitems);
3504 return this.menuitems.get(t.id);
3506 //return this.items.get(t.menuItemId);
3512 onTouch : function(e)
3514 Roo.log("menu.onTouch");
3515 //e.stopEvent(); this make the user popdown broken
3519 onClick : function(e)
3521 Roo.log("menu.onClick");
3523 var t = this.findTargetItem(e);
3524 if(!t || t.isContainer){
3529 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3530 if(t == this.activeItem && t.shouldDeactivate(e)){
3531 this.activeItem.deactivate();
3532 delete this.activeItem;
3536 this.setActiveItem(t, true);
3544 Roo.log('pass click event');
3548 this.fireEvent("click", this, t, e);
3552 if(!t.href.length || t.href == '#'){
3553 (function() { _this.hide(); }).defer(100);
3558 onMouseOver : function(e){
3559 var t = this.findTargetItem(e);
3562 // if(t.canActivate && !t.disabled){
3563 // this.setActiveItem(t, true);
3567 this.fireEvent("mouseover", this, e, t);
3569 isVisible : function(){
3570 return !this.hidden;
3572 onMouseOut : function(e){
3573 var t = this.findTargetItem(e);
3576 // if(t == this.activeItem && t.shouldDeactivate(e)){
3577 // this.activeItem.deactivate();
3578 // delete this.activeItem;
3581 this.fireEvent("mouseout", this, e, t);
3586 * Displays this menu relative to another element
3587 * @param {String/HTMLElement/Roo.Element} element The element to align to
3588 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589 * the element (defaults to this.defaultAlign)
3590 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3592 show : function(el, pos, parentMenu)
3594 if (false === this.fireEvent("beforeshow", this)) {
3595 Roo.log("show canceled");
3598 this.parentMenu = parentMenu;
3603 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3606 * Displays this menu at a specific xy position
3607 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3610 showAt : function(xy, parentMenu, /* private: */_e){
3611 this.parentMenu = parentMenu;
3616 this.fireEvent("beforeshow", this);
3617 //xy = this.el.adjustForConstraints(xy);
3621 this.hideMenuItems();
3622 this.hidden = false;
3623 this.triggerEl.addClass('open');
3624 this.el.addClass('show');
3626 // reassign x when hitting right
3627 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3631 // reassign y when hitting bottom
3632 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3636 // but the list may align on trigger left or trigger top... should it be a properity?
3638 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3643 this.fireEvent("show", this);
3649 this.doFocus.defer(50, this);
3653 doFocus : function(){
3655 this.focusEl.focus();
3660 * Hides this menu and optionally all parent menus
3661 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3663 hide : function(deep)
3665 if (false === this.fireEvent("beforehide", this)) {
3666 Roo.log("hide canceled");
3669 this.hideMenuItems();
3670 if(this.el && this.isVisible()){
3672 if(this.activeItem){
3673 this.activeItem.deactivate();
3674 this.activeItem = null;
3676 this.triggerEl.removeClass('open');;
3677 this.el.removeClass('show');
3679 this.fireEvent("hide", this);
3681 if(deep === true && this.parentMenu){
3682 this.parentMenu.hide(true);
3686 onTriggerClick : function(e)
3688 Roo.log('trigger click');
3690 var target = e.getTarget();
3692 Roo.log(target.nodeName.toLowerCase());
3694 if(target.nodeName.toLowerCase() === 'i'){
3700 onTriggerPress : function(e)
3702 Roo.log('trigger press');
3703 //Roo.log(e.getTarget());
3704 // Roo.log(this.triggerEl.dom);
3706 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707 var pel = Roo.get(e.getTarget());
3708 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709 Roo.log('is treeview or dropdown?');
3713 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3717 if (this.isVisible()) {
3722 this.show(this.triggerEl, '?', false);
3725 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3732 hideMenuItems : function()
3734 Roo.log("hide Menu Items");
3739 this.el.select('.open',true).each(function(aa) {
3741 aa.removeClass('open');
3745 addxtypeChild : function (tree, cntr) {
3746 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3748 this.menuitems.add(comp);
3760 this.getEl().dom.innerHTML = '';
3761 this.menuitems.clear();
3775 * @class Roo.bootstrap.MenuItem
3776 * @extends Roo.bootstrap.Component
3777 * Bootstrap MenuItem class
3778 * @cfg {String} html the menu label
3779 * @cfg {String} href the link
3780 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782 * @cfg {Boolean} active used on sidebars to highlight active itesm
3783 * @cfg {String} fa favicon to show on left of menu item.
3784 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3788 * Create a new MenuItem
3789 * @param {Object} config The config object
3793 Roo.bootstrap.MenuItem = function(config){
3794 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3799 * The raw click event for the entire grid.
3800 * @param {Roo.bootstrap.MenuItem} this
3801 * @param {Roo.EventObject} e
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3811 preventDefault: false,
3812 isContainer : false,
3816 getAutoCreate : function(){
3818 if(this.isContainer){
3821 cls: 'dropdown-menu-item '
3831 cls : 'dropdown-item',
3836 if (this.fa !== false) {
3839 cls : 'fa fa-' + this.fa
3848 cls: 'dropdown-menu-item',
3851 if (this.parent().type == 'treeview') {
3852 cfg.cls = 'treeview-menu';
3855 cfg.cls += ' active';
3860 anc.href = this.href || cfg.cn[0].href ;
3861 ctag.html = this.html || cfg.cn[0].html ;
3865 initEvents: function()
3867 if (this.parent().type == 'treeview') {
3868 this.el.select('a').on('click', this.onClick, this);
3872 this.menu.parentType = this.xtype;
3873 this.menu.triggerEl = this.el;
3874 this.menu = this.addxtype(Roo.apply({}, this.menu));
3878 onClick : function(e)
3880 Roo.log('item on click ');
3882 if(this.preventDefault){
3885 //this.parent().hideMenuItems();
3887 this.fireEvent('click', this, e);
3906 * @class Roo.bootstrap.MenuSeparator
3907 * @extends Roo.bootstrap.Component
3908 * Bootstrap MenuSeparator class
3911 * Create a new MenuItem
3912 * @param {Object} config The config object
3916 Roo.bootstrap.MenuSeparator = function(config){
3917 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3922 getAutoCreate : function(){
3941 * @class Roo.bootstrap.Modal
3942 * @extends Roo.bootstrap.Component
3943 * Bootstrap Modal class
3944 * @cfg {String} title Title of dialog
3945 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3947 * @cfg {Boolean} specificTitle default false
3948 * @cfg {Array} buttons Array of buttons or standard button set..
3949 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950 * @cfg {Boolean} animate default true
3951 * @cfg {Boolean} allow_close default true
3952 * @cfg {Boolean} fitwindow default false
3953 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956 * @cfg {String} size (sm|lg|xl) default empty
3957 * @cfg {Number} max_width set the max width of modal
3958 * @cfg {Boolean} editableTitle can the title be edited
3963 * Create a new Modal Dialog
3964 * @param {Object} config The config object
3967 Roo.bootstrap.Modal = function(config){
3968 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3973 * The raw btnclick event for the button
3974 * @param {Roo.EventObject} e
3979 * Fire when dialog resize
3980 * @param {Roo.bootstrap.Modal} this
3981 * @param {Roo.EventObject} e
3985 * @event titlechanged
3986 * Fire when the editable title has been changed
3987 * @param {Roo.bootstrap.Modal} this
3988 * @param {Roo.EventObject} value
3990 "titlechanged" : true
3993 this.buttons = this.buttons || [];
3996 this.tmpl = Roo.factory(this.tmpl);
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4003 title : 'test dialog',
4013 specificTitle: false,
4015 buttonPosition: 'right',
4037 editableTitle : false,
4039 onRender : function(ct, position)
4041 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4044 var cfg = Roo.apply({}, this.getAutoCreate());
4047 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4049 //if (!cfg.name.length) {
4053 cfg.cls += ' ' + this.cls;
4056 cfg.style = this.style;
4058 this.el = Roo.get(document.body).createChild(cfg, position);
4060 //var type = this.el.dom.type;
4063 if(this.tabIndex !== undefined){
4064 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4067 this.dialogEl = this.el.select('.modal-dialog',true).first();
4068 this.bodyEl = this.el.select('.modal-body',true).first();
4069 this.closeEl = this.el.select('.modal-header .close', true).first();
4070 this.headerEl = this.el.select('.modal-header',true).first();
4071 this.titleEl = this.el.select('.modal-title',true).first();
4072 this.footerEl = this.el.select('.modal-footer',true).first();
4074 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4076 //this.el.addClass("x-dlg-modal");
4078 if (this.buttons.length) {
4079 Roo.each(this.buttons, function(bb) {
4080 var b = Roo.apply({}, bb);
4081 b.xns = b.xns || Roo.bootstrap;
4082 b.xtype = b.xtype || 'Button';
4083 if (typeof(b.listeners) == 'undefined') {
4084 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4087 var btn = Roo.factory(b);
4089 btn.render(this.getButtonContainer());
4093 // render the children.
4096 if(typeof(this.items) != 'undefined'){
4097 var items = this.items;
4100 for(var i =0;i < items.length;i++) {
4101 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4105 this.items = nitems;
4107 // where are these used - they used to be body/close/footer
4111 //this.el.addClass([this.fieldClass, this.cls]);
4115 getAutoCreate : function()
4117 // we will default to modal-body-overflow - might need to remove or make optional later.
4119 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4120 html : this.html || ''
4125 cls : 'modal-title',
4129 if(this.specificTitle){ // WTF is this?
4134 if (this.allow_close && Roo.bootstrap.version == 3) {
4144 if (this.editableTitle) {
4146 cls: 'form-control roo-editable-title d-none',
4152 if (this.allow_close && Roo.bootstrap.version == 4) {
4162 if(this.size.length){
4163 size = 'modal-' + this.size;
4166 var footer = Roo.bootstrap.version == 3 ?
4168 cls : 'modal-footer',
4172 cls: 'btn-' + this.buttonPosition
4177 { // BS4 uses mr-auto on left buttons....
4178 cls : 'modal-footer'
4189 cls: "modal-dialog " + size,
4192 cls : "modal-content",
4195 cls : 'modal-header',
4210 modal.cls += ' fade';
4216 getChildContainer : function() {
4221 getButtonContainer : function() {
4223 return Roo.bootstrap.version == 4 ?
4224 this.el.select('.modal-footer',true).first()
4225 : this.el.select('.modal-footer div',true).first();
4228 initEvents : function()
4230 if (this.allow_close) {
4231 this.closeEl.on('click', this.hide, this);
4233 Roo.EventManager.onWindowResize(this.resize, this, true);
4234 if (this.editableTitle) {
4235 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4236 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237 this.headerEditEl.on('keyup', function(e) {
4238 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239 this.toggleHeaderInput(false)
4242 this.headerEditEl.on('blur', function(e) {
4243 this.toggleHeaderInput(false)
4252 this.maskEl.setSize(
4253 Roo.lib.Dom.getViewWidth(true),
4254 Roo.lib.Dom.getViewHeight(true)
4257 if (this.fitwindow) {
4259 this.dialogEl.setStyle( { 'max-width' : '100%' });
4261 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4267 if(this.max_width !== 0) {
4269 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4272 this.setSize(w, this.height);
4276 if(this.max_height) {
4277 this.setSize(w,Math.min(
4279 Roo.lib.Dom.getViewportHeight(true) - 60
4285 if(!this.fit_content) {
4286 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4290 this.setSize(w, Math.min(
4292 this.headerEl.getHeight() +
4293 this.footerEl.getHeight() +
4294 this.getChildHeight(this.bodyEl.dom.childNodes),
4295 Roo.lib.Dom.getViewportHeight(true) - 60)
4301 setSize : function(w,h)
4312 if (!this.rendered) {
4315 this.toggleHeaderInput(false);
4316 //this.el.setStyle('display', 'block');
4317 this.el.removeClass('hideing');
4318 this.el.dom.style.display='block';
4320 Roo.get(document.body).addClass('modal-open');
4322 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4325 this.el.addClass('show');
4326 this.el.addClass('in');
4329 this.el.addClass('show');
4330 this.el.addClass('in');
4333 // not sure how we can show data in here..
4335 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4338 Roo.get(document.body).addClass("x-body-masked");
4340 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4341 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342 this.maskEl.dom.style.display = 'block';
4343 this.maskEl.addClass('show');
4348 this.fireEvent('show', this);
4350 // set zindex here - otherwise it appears to be ignored...
4351 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4354 this.items.forEach( function(e) {
4355 e.layout ? e.layout() : false;
4363 if(this.fireEvent("beforehide", this) !== false){
4365 this.maskEl.removeClass('show');
4367 this.maskEl.dom.style.display = '';
4368 Roo.get(document.body).removeClass("x-body-masked");
4369 this.el.removeClass('in');
4370 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4372 if(this.animate){ // why
4373 this.el.addClass('hideing');
4374 this.el.removeClass('show');
4376 if (!this.el.hasClass('hideing')) {
4377 return; // it's been shown again...
4380 this.el.dom.style.display='';
4382 Roo.get(document.body).removeClass('modal-open');
4383 this.el.removeClass('hideing');
4387 this.el.removeClass('show');
4388 this.el.dom.style.display='';
4389 Roo.get(document.body).removeClass('modal-open');
4392 this.fireEvent('hide', this);
4395 isVisible : function()
4398 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4402 addButton : function(str, cb)
4406 var b = Roo.apply({}, { html : str } );
4407 b.xns = b.xns || Roo.bootstrap;
4408 b.xtype = b.xtype || 'Button';
4409 if (typeof(b.listeners) == 'undefined') {
4410 b.listeners = { click : cb.createDelegate(this) };
4413 var btn = Roo.factory(b);
4415 btn.render(this.getButtonContainer());
4421 setDefaultButton : function(btn)
4423 //this.el.select('.modal-footer').()
4426 resizeTo: function(w,h)
4428 this.dialogEl.setWidth(w);
4430 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4432 this.bodyEl.setHeight(h - diff);
4434 this.fireEvent('resize', this);
4437 setContentSize : function(w, h)
4441 onButtonClick: function(btn,e)
4444 this.fireEvent('btnclick', btn.name, e);
4447 * Set the title of the Dialog
4448 * @param {String} str new Title
4450 setTitle: function(str) {
4451 this.titleEl.dom.innerHTML = str;
4455 * Set the body of the Dialog
4456 * @param {String} str new Title
4458 setBody: function(str) {
4459 this.bodyEl.dom.innerHTML = str;
4462 * Set the body of the Dialog using the template
4463 * @param {Obj} data - apply this data to the template and replace the body contents.
4465 applyBody: function(obj)
4468 Roo.log("Error - using apply Body without a template");
4471 this.tmpl.overwrite(this.bodyEl, obj);
4474 getChildHeight : function(child_nodes)
4478 child_nodes.length == 0
4483 var child_height = 0;
4485 for(var i = 0; i < child_nodes.length; i++) {
4488 * for modal with tabs...
4489 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4491 var layout_childs = child_nodes[i].childNodes;
4493 for(var j = 0; j < layout_childs.length; j++) {
4495 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4497 var layout_body_childs = layout_childs[j].childNodes;
4499 for(var k = 0; k < layout_body_childs.length; k++) {
4501 if(layout_body_childs[k].classList.contains('navbar')) {
4502 child_height += layout_body_childs[k].offsetHeight;
4506 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4508 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4510 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4512 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4528 child_height += child_nodes[i].offsetHeight;
4529 // Roo.log(child_nodes[i].offsetHeight);
4532 return child_height;
4534 toggleHeaderInput : function(is_edit)
4536 if (!this.editableTitle) {
4537 return; // not editable.
4539 if (is_edit && this.is_header_editing) {
4540 return; // already editing..
4544 this.headerEditEl.dom.value = this.title;
4545 this.headerEditEl.removeClass('d-none');
4546 this.headerEditEl.dom.focus();
4547 this.titleEl.addClass('d-none');
4549 this.is_header_editing = true;
4552 // flip back to not editing.
4553 this.title = this.headerEditEl.dom.value;
4554 this.headerEditEl.addClass('d-none');
4555 this.titleEl.removeClass('d-none');
4556 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557 this.is_header_editing = false;
4558 this.fireEvent('titlechanged', this, this.title);
4567 Roo.apply(Roo.bootstrap.Modal, {
4569 * Button config that displays a single OK button
4578 * Button config that displays Yes and No buttons
4594 * Button config that displays OK and Cancel buttons
4609 * Button config that displays Yes, No and Cancel buttons
4634 * messagebox - can be used as a replace
4638 * @class Roo.MessageBox
4639 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4648 // process text value...
4652 // Show a dialog using config options:
4654 title:'Save Changes?',
4655 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656 buttons: Roo.Msg.YESNOCANCEL,
4663 Roo.bootstrap.MessageBox = function(){
4664 var dlg, opt, mask, waitTimer;
4665 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666 var buttons, activeTextEl, bwidth;
4670 var handleButton = function(button){
4672 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4676 var handleHide = function(){
4678 dlg.el.removeClass(opt.cls);
4681 // Roo.TaskMgr.stop(waitTimer);
4682 // waitTimer = null;
4687 var updateButtons = function(b){
4690 buttons["ok"].hide();
4691 buttons["cancel"].hide();
4692 buttons["yes"].hide();
4693 buttons["no"].hide();
4694 dlg.footerEl.hide();
4698 dlg.footerEl.show();
4699 for(var k in buttons){
4700 if(typeof buttons[k] != "function"){
4703 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704 width += buttons[k].el.getWidth()+15;
4714 var handleEsc = function(d, k, e){
4715 if(opt && opt.closable !== false){
4725 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726 * @return {Roo.BasicDialog} The BasicDialog element
4728 getDialog : function(){
4730 dlg = new Roo.bootstrap.Modal( {
4733 //constraintoviewport:false,
4735 //collapsible : false,
4740 //buttonAlign:"center",
4741 closeClick : function(){
4742 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4745 handleButton("cancel");
4750 dlg.on("hide", handleHide);
4752 //dlg.addKeyListener(27, handleEsc);
4754 this.buttons = buttons;
4755 var bt = this.buttonText;
4756 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4761 bodyEl = dlg.bodyEl.createChild({
4763 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764 '<textarea class="roo-mb-textarea"></textarea>' +
4765 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4767 msgEl = bodyEl.dom.firstChild;
4768 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769 textboxEl.enableDisplayMode();
4770 textboxEl.addKeyListener([10,13], function(){
4771 if(dlg.isVisible() && opt && opt.buttons){
4774 }else if(opt.buttons.yes){
4775 handleButton("yes");
4779 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780 textareaEl.enableDisplayMode();
4781 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782 progressEl.enableDisplayMode();
4784 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785 var pf = progressEl.dom.firstChild;
4787 pp = Roo.get(pf.firstChild);
4788 pp.setHeight(pf.offsetHeight);
4796 * Updates the message box body text
4797 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798 * the XHTML-compliant non-breaking space character '&#160;')
4799 * @return {Roo.MessageBox} This message box
4801 updateText : function(text)
4803 if(!dlg.isVisible() && !opt.width){
4804 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4807 msgEl.innerHTML = text || ' ';
4809 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4812 Math.min(opt.width || cw , this.maxWidth),
4813 Math.max(opt.minWidth || this.minWidth, bwidth)
4816 activeTextEl.setWidth(w);
4818 if(dlg.isVisible()){
4819 dlg.fixedcenter = false;
4821 // to big, make it scroll. = But as usual stupid IE does not support
4824 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4828 bodyEl.dom.style.height = '';
4829 bodyEl.dom.style.overflowY = '';
4832 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4834 bodyEl.dom.style.overflowX = '';
4837 dlg.setContentSize(w, bodyEl.getHeight());
4838 if(dlg.isVisible()){
4839 dlg.fixedcenter = true;
4845 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4846 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849 * @return {Roo.MessageBox} This message box
4851 updateProgress : function(value, text){
4853 this.updateText(text);
4856 if (pp) { // weird bug on my firefox - for some reason this is not defined
4857 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4864 * Returns true if the message box is currently displayed
4865 * @return {Boolean} True if the message box is visible, else false
4867 isVisible : function(){
4868 return dlg && dlg.isVisible();
4872 * Hides the message box if it is displayed
4875 if(this.isVisible()){
4881 * Displays a new message box, or reinitializes an existing message box, based on the config options
4882 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883 * The following config object properties are supported:
4885 Property Type Description
4886 ---------- --------------- ------------------------------------------------------------------------------------
4887 animEl String/Element An id or Element from which the message box should animate as it opens and
4888 closes (defaults to undefined)
4889 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable Boolean False to hide the top-right close button (defaults to true). Note that
4892 progress and wait dialogs will ignore this property and always hide the
4893 close button as they can only be closed programmatically.
4894 cls String A custom CSS class to apply to the message box element
4895 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4896 displayed (defaults to 75)
4897 fn Function A callback function to execute after closing the dialog. The arguments to the
4898 function will be btn (the name of the button that was clicked, if applicable,
4899 e.g. "ok"), and text (the value of the active text field, if applicable).
4900 Progress and wait dialogs will ignore this option since they do not respond to
4901 user actions and can only be closed programmatically, so any required function
4902 should be called by the same code after it closes the dialog.
4903 icon String A CSS class that provides a background image to be used as an icon for
4904 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4906 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4907 modal Boolean False to allow user interaction with the page while the message box is
4908 displayed (defaults to true)
4909 msg String A string that will replace the existing message box body text (defaults
4910 to the XHTML-compliant non-breaking space character ' ')
4911 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4912 progress Boolean True to display a progress bar (defaults to false)
4913 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4916 title String The title text
4917 value String The string value to set into the active textbox element if displayed
4918 wait Boolean True to display a progress bar (defaults to false)
4919 width Number The width of the dialog in pixels
4926 msg: 'Please enter your address:',
4928 buttons: Roo.MessageBox.OKCANCEL,
4931 animEl: 'addAddressBtn'
4934 * @param {Object} config Configuration options
4935 * @return {Roo.MessageBox} This message box
4937 show : function(options)
4940 // this causes nightmares if you show one dialog after another
4941 // especially on callbacks..
4943 if(this.isVisible()){
4946 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4948 Roo.log("New Dialog Message:" + options.msg )
4949 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4953 var d = this.getDialog();
4955 d.setTitle(opt.title || " ");
4956 d.closeEl.setDisplayed(opt.closable !== false);
4957 activeTextEl = textboxEl;
4958 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4963 textareaEl.setHeight(typeof opt.multiline == "number" ?
4964 opt.multiline : this.defaultTextHeight);
4965 activeTextEl = textareaEl;
4974 progressEl.setDisplayed(opt.progress === true);
4976 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4978 this.updateProgress(0);
4979 activeTextEl.dom.value = opt.value || "";
4981 dlg.setDefaultButton(activeTextEl);
4983 var bs = opt.buttons;
4987 }else if(bs && bs.yes){
4988 db = buttons["yes"];
4990 dlg.setDefaultButton(db);
4992 bwidth = updateButtons(opt.buttons);
4993 this.updateText(opt.msg);
4995 d.el.addClass(opt.cls);
4997 d.proxyDrag = opt.proxyDrag === true;
4998 d.modal = opt.modal !== false;
4999 d.mask = opt.modal !== false ? mask : false;
5001 // force it to the end of the z-index stack so it gets a cursor in FF
5002 document.body.appendChild(dlg.el.dom);
5003 d.animateTarget = null;
5004 d.show(options.animEl);
5010 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5011 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012 * and closing the message box when the process is complete.
5013 * @param {String} title The title bar text
5014 * @param {String} msg The message box body text
5015 * @return {Roo.MessageBox} This message box
5017 progress : function(title, msg){
5024 minWidth: this.minProgressWidth,
5031 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032 * If a callback function is passed it will be called after the user clicks the button, and the
5033 * id of the button that was clicked will be passed as the only parameter to the callback
5034 * (could also be the top-right close button).
5035 * @param {String} title The title bar text
5036 * @param {String} msg The message box body text
5037 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038 * @param {Object} scope (optional) The scope of the callback function
5039 * @return {Roo.MessageBox} This message box
5041 alert : function(title, msg, fn, scope)
5056 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5057 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058 * You are responsible for closing the message box when the process is complete.
5059 * @param {String} msg The message box body text
5060 * @param {String} title (optional) The title bar text
5061 * @return {Roo.MessageBox} This message box
5063 wait : function(msg, title){
5074 waitTimer = Roo.TaskMgr.start({
5076 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5084 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087 * @param {String} title The title bar text
5088 * @param {String} msg The message box body text
5089 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090 * @param {Object} scope (optional) The scope of the callback function
5091 * @return {Roo.MessageBox} This message box
5093 confirm : function(title, msg, fn, scope){
5097 buttons: this.YESNO,
5106 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5108 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109 * (could also be the top-right close button) and the text that was entered will be passed as the two
5110 * parameters to the callback.
5111 * @param {String} title The title bar text
5112 * @param {String} msg The message box body text
5113 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114 * @param {Object} scope (optional) The scope of the callback function
5115 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117 * @return {Roo.MessageBox} This message box
5119 prompt : function(title, msg, fn, scope, multiline){
5123 buttons: this.OKCANCEL,
5128 multiline: multiline,
5135 * Button config that displays a single OK button
5140 * Button config that displays Yes and No buttons
5143 YESNO : {yes:true, no:true},
5145 * Button config that displays OK and Cancel buttons
5148 OKCANCEL : {ok:true, cancel:true},
5150 * Button config that displays Yes, No and Cancel buttons
5153 YESNOCANCEL : {yes:true, no:true, cancel:true},
5156 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5159 defaultTextHeight : 75,
5161 * The maximum width in pixels of the message box (defaults to 600)
5166 * The minimum width in pixels of the message box (defaults to 100)
5171 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5172 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5175 minProgressWidth : 250,
5177 * An object containing the default button text strings that can be overriden for localized language support.
5178 * Supported properties are: ok, cancel, yes and no.
5179 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5192 * Shorthand for {@link Roo.MessageBox}
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5204 * @class Roo.bootstrap.Navbar
5205 * @extends Roo.bootstrap.Component
5206 * Bootstrap Navbar class
5209 * Create a new Navbar
5210 * @param {Object} config The config object
5214 Roo.bootstrap.Navbar = function(config){
5215 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5219 * @event beforetoggle
5220 * Fire before toggle the menu
5221 * @param {Roo.EventObject} e
5223 "beforetoggle" : true
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5236 getAutoCreate : function(){
5239 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5243 initEvents :function ()
5245 //Roo.log(this.el.select('.navbar-toggle',true));
5246 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5253 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5255 var size = this.el.getSize();
5256 this.maskEl.setSize(size.width, size.height);
5257 this.maskEl.enableDisplayMode("block");
5266 getChildContainer : function()
5268 if (this.el && this.el.select('.collapse').getCount()) {
5269 return this.el.select('.collapse',true).first();
5284 onToggle : function()
5287 if(this.fireEvent('beforetoggle', this) === false){
5290 var ce = this.el.select('.navbar-collapse',true).first();
5292 if (!ce.hasClass('show')) {
5302 * Expand the navbar pulldown
5304 expand : function ()
5307 var ce = this.el.select('.navbar-collapse',true).first();
5308 if (ce.hasClass('collapsing')) {
5311 ce.dom.style.height = '';
5313 ce.addClass('in'); // old...
5314 ce.removeClass('collapse');
5315 ce.addClass('show');
5316 var h = ce.getHeight();
5318 ce.removeClass('show');
5319 // at this point we should be able to see it..
5320 ce.addClass('collapsing');
5322 ce.setHeight(0); // resize it ...
5323 ce.on('transitionend', function() {
5324 //Roo.log('done transition');
5325 ce.removeClass('collapsing');
5326 ce.addClass('show');
5327 ce.removeClass('collapse');
5329 ce.dom.style.height = '';
5330 }, this, { single: true} );
5332 ce.dom.scrollTop = 0;
5335 * Collapse the navbar pulldown
5337 collapse : function()
5339 var ce = this.el.select('.navbar-collapse',true).first();
5341 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342 // it's collapsed or collapsing..
5345 ce.removeClass('in'); // old...
5346 ce.setHeight(ce.getHeight());
5347 ce.removeClass('show');
5348 ce.addClass('collapsing');
5350 ce.on('transitionend', function() {
5351 ce.dom.style.height = '';
5352 ce.removeClass('collapsing');
5353 ce.addClass('collapse');
5354 }, this, { single: true} );
5374 * @class Roo.bootstrap.NavSimplebar
5375 * @extends Roo.bootstrap.Navbar
5376 * Bootstrap Sidebar class
5378 * @cfg {Boolean} inverse is inverted color
5380 * @cfg {String} type (nav | pills | tabs)
5381 * @cfg {Boolean} arrangement stacked | justified
5382 * @cfg {String} align (left | right) alignment
5384 * @cfg {Boolean} main (true|false) main nav bar? default false
5385 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5387 * @cfg {String} tag (header|footer|nav|div) default is nav
5389 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5393 * Create a new Sidebar
5394 * @param {Object} config The config object
5398 Roo.bootstrap.NavSimplebar = function(config){
5399 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5418 getAutoCreate : function(){
5422 tag : this.tag || 'div',
5423 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5425 if (['light','white'].indexOf(this.weight) > -1) {
5426 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5428 cfg.cls += ' bg-' + this.weight;
5431 cfg.cls += ' navbar-inverse';
5435 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5437 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5446 cls: 'nav nav-' + this.xtype,
5452 this.type = this.type || 'nav';
5453 if (['tabs','pills'].indexOf(this.type) != -1) {
5454 cfg.cn[0].cls += ' nav-' + this.type
5458 if (this.type!=='nav') {
5459 Roo.log('nav type must be nav/tabs/pills')
5461 cfg.cn[0].cls += ' navbar-nav'
5467 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468 cfg.cn[0].cls += ' nav-' + this.arrangement;
5472 if (this.align === 'right') {
5473 cfg.cn[0].cls += ' navbar-right';
5498 * navbar-expand-md fixed-top
5502 * @class Roo.bootstrap.NavHeaderbar
5503 * @extends Roo.bootstrap.NavSimplebar
5504 * Bootstrap Sidebar class
5506 * @cfg {String} brand what is brand
5507 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508 * @cfg {String} brand_href href of the brand
5509 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5510 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5515 * Create a new Sidebar
5516 * @param {Object} config The config object
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5532 desktopCenter : false,
5535 getAutoCreate : function(){
5538 tag: this.nav || 'nav',
5539 cls: 'navbar navbar-expand-md',
5545 if (this.desktopCenter) {
5546 cn.push({cls : 'container', cn : []});
5554 cls: 'navbar-toggle navbar-toggler',
5555 'data-toggle': 'collapse',
5560 html: 'Toggle navigation'
5564 cls: 'icon-bar navbar-toggler-icon'
5577 cn.push( Roo.bootstrap.version == 4 ? btn : {
5579 cls: 'navbar-header',
5588 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5592 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5594 if (['light','white'].indexOf(this.weight) > -1) {
5595 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5597 cfg.cls += ' bg-' + this.weight;
5600 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5603 // tag can override this..
5605 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5608 if (this.brand !== '') {
5609 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5612 href: this.brand_href ? this.brand_href : '#',
5613 cls: 'navbar-brand',
5621 cfg.cls += ' main-nav';
5629 getHeaderChildContainer : function()
5631 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632 return this.el.select('.navbar-header',true).first();
5635 return this.getChildContainer();
5638 getChildContainer : function()
5641 return this.el.select('.roo-navbar-collapse',true).first();
5646 initEvents : function()
5648 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5650 if (this.autohide) {
5655 Roo.get(document).on('scroll',function(e) {
5656 var ns = Roo.get(document).getScroll().top;
5657 var os = prevScroll;
5661 ft.removeClass('slideDown');
5662 ft.addClass('slideUp');
5665 ft.removeClass('slideUp');
5666 ft.addClass('slideDown');
5687 * @class Roo.bootstrap.NavSidebar
5688 * @extends Roo.bootstrap.Navbar
5689 * Bootstrap Sidebar class
5692 * Create a new Sidebar
5693 * @param {Object} config The config object
5697 Roo.bootstrap.NavSidebar = function(config){
5698 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5703 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5705 getAutoCreate : function(){
5710 cls: 'sidebar sidebar-nav'
5732 * @class Roo.bootstrap.NavGroup
5733 * @extends Roo.bootstrap.Component
5734 * Bootstrap NavGroup class
5735 * @cfg {String} align (left|right)
5736 * @cfg {Boolean} inverse
5737 * @cfg {String} type (nav|pills|tab) default nav
5738 * @cfg {String} navId - reference Id for navbar.
5739 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5742 * Create a new nav group
5743 * @param {Object} config The config object
5746 Roo.bootstrap.NavGroup = function(config){
5747 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5750 Roo.bootstrap.NavGroup.register(this);
5754 * Fires when the active item changes
5755 * @param {Roo.bootstrap.NavGroup} this
5756 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5776 getAutoCreate : function()
5778 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5784 if (Roo.bootstrap.version == 4) {
5785 if (['tabs','pills'].indexOf(this.type) != -1) {
5786 cfg.cls += ' nav-' + this.type;
5788 // trying to remove so header bar can right align top?
5789 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790 // do not use on header bar...
5791 cfg.cls += ' navbar-nav';
5796 if (['tabs','pills'].indexOf(this.type) != -1) {
5797 cfg.cls += ' nav-' + this.type
5799 if (this.type !== 'nav') {
5800 Roo.log('nav type must be nav/tabs/pills')
5802 cfg.cls += ' navbar-nav'
5806 if (this.parent() && this.parent().sidebar) {
5809 cls: 'dashboard-menu sidebar-menu'
5815 if (this.form === true) {
5818 cls: 'navbar-form form-inline'
5820 //nav navbar-right ml-md-auto
5821 if (this.align === 'right') {
5822 cfg.cls += ' navbar-right ml-md-auto';
5824 cfg.cls += ' navbar-left';
5828 if (this.align === 'right') {
5829 cfg.cls += ' navbar-right ml-md-auto';
5831 cfg.cls += ' mr-auto';
5835 cfg.cls += ' navbar-inverse';
5843 * sets the active Navigation item
5844 * @param {Roo.bootstrap.NavItem} the new current navitem
5846 setActiveItem : function(item)
5849 Roo.each(this.navItems, function(v){
5854 v.setActive(false, true);
5861 item.setActive(true, true);
5862 this.fireEvent('changed', this, item, prev);
5867 * gets the active Navigation item
5868 * @return {Roo.bootstrap.NavItem} the current navitem
5870 getActive : function()
5874 Roo.each(this.navItems, function(v){
5885 indexOfNav : function()
5889 Roo.each(this.navItems, function(v,i){
5900 * adds a Navigation item
5901 * @param {Roo.bootstrap.NavItem} the navitem to add
5903 addItem : function(cfg)
5905 if (this.form && Roo.bootstrap.version == 4) {
5908 var cn = new Roo.bootstrap.NavItem(cfg);
5910 cn.parentId = this.id;
5911 cn.onRender(this.el, null);
5915 * register a Navigation item
5916 * @param {Roo.bootstrap.NavItem} the navitem to add
5918 register : function(item)
5920 this.navItems.push( item);
5921 item.navId = this.navId;
5926 * clear all the Navigation item
5929 clearAll : function()
5932 this.el.dom.innerHTML = '';
5935 getNavItem: function(tabId)
5938 Roo.each(this.navItems, function(e) {
5939 if (e.tabId == tabId) {
5949 setActiveNext : function()
5951 var i = this.indexOfNav(this.getActive());
5952 if (i > this.navItems.length) {
5955 this.setActiveItem(this.navItems[i+1]);
5957 setActivePrev : function()
5959 var i = this.indexOfNav(this.getActive());
5963 this.setActiveItem(this.navItems[i-1]);
5965 clearWasActive : function(except) {
5966 Roo.each(this.navItems, function(e) {
5967 if (e.tabId != except.tabId && e.was_active) {
5968 e.was_active = false;
5975 getWasActive : function ()
5978 Roo.each(this.navItems, function(e) {
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5997 * register a Navigation Group
5998 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6000 register : function(navgrp)
6002 this.groups[navgrp.navId] = navgrp;
6006 * fetch a Navigation Group based on the navigation ID
6007 * @param {string} the navgroup to add
6008 * @returns {Roo.bootstrap.NavGroup} the navgroup
6010 get: function(navId) {
6011 if (typeof(this.groups[navId]) == 'undefined') {
6013 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6015 return this.groups[navId] ;
6030 * @class Roo.bootstrap.NavItem
6031 * @extends Roo.bootstrap.Component
6032 * Bootstrap Navbar.NavItem class
6033 * @cfg {String} href link to
6034 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035 * @cfg {Boolean} button_outline show and outlined button
6036 * @cfg {String} html content of button
6037 * @cfg {String} badge text inside badge
6038 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039 * @cfg {String} glyphicon DEPRICATED - use fa
6040 * @cfg {String} icon DEPRICATED - use fa
6041 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042 * @cfg {Boolean} active Is item active
6043 * @cfg {Boolean} disabled Is item disabled
6044 * @cfg {String} linkcls Link Class
6045 * @cfg {Boolean} preventDefault (true | false) default false
6046 * @cfg {String} tabId the tab that this item activates.
6047 * @cfg {String} tagtype (a|span) render as a href or span?
6048 * @cfg {Boolean} animateRef (true|false) link to element default false
6051 * Create a new Navbar Item
6052 * @param {Object} config The config object
6054 Roo.bootstrap.NavItem = function(config){
6055 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6060 * The raw click event for the entire grid.
6061 * @param {Roo.EventObject} e
6066 * Fires when the active item active state changes
6067 * @param {Roo.bootstrap.NavItem} this
6068 * @param {boolean} state the new state
6074 * Fires when scroll to element
6075 * @param {Roo.bootstrap.NavItem} this
6076 * @param {Object} options
6077 * @param {Roo.EventObject} e
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6094 preventDefault : false,
6102 button_outline : false,
6106 getAutoCreate : function(){
6113 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6116 cfg.cls += ' active' ;
6118 if (this.disabled) {
6119 cfg.cls += ' disabled';
6123 if (this.button_weight.length) {
6124 cfg.tag = this.href ? 'a' : 'button';
6125 cfg.html = this.html || '';
6126 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6128 cfg.href = this.href;
6131 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6134 // menu .. should add dropdown-menu class - so no need for carat..
6136 if (this.badge !== '') {
6138 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6143 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6147 href : this.href || "#",
6148 html: this.html || ''
6151 if (this.tagtype == 'a') {
6152 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6156 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6159 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6161 if(this.glyphicon) {
6162 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6167 cfg.cn[0].html += " <span class='caret'></span>";
6171 if (this.badge !== '') {
6173 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6181 onRender : function(ct, position)
6183 // Roo.log("Call onRender: " + this.xtype);
6184 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6188 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189 this.navLink = this.el.select('.nav-link',true).first();
6194 initEvents: function()
6196 if (typeof (this.menu) != 'undefined') {
6197 this.menu.parentType = this.xtype;
6198 this.menu.triggerEl = this.el;
6199 this.menu = this.addxtype(Roo.apply({}, this.menu));
6202 this.el.on('click', this.onClick, this);
6204 //if(this.tagtype == 'span'){
6205 // this.el.select('span',true).on('click', this.onClick, this);
6208 // at this point parent should be available..
6209 this.parent().register(this);
6212 onClick : function(e)
6214 if (e.getTarget('.dropdown-menu-item')) {
6215 // did you click on a menu itemm.... - then don't trigger onclick..
6220 this.preventDefault ||
6223 Roo.log("NavItem - prevent Default?");
6227 if (this.disabled) {
6231 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232 if (tg && tg.transition) {
6233 Roo.log("waiting for the transitionend");
6239 //Roo.log("fire event clicked");
6240 if(this.fireEvent('click', this, e) === false){
6244 if(this.tagtype == 'span'){
6248 //Roo.log(this.href);
6249 var ael = this.el.select('a',true).first();
6252 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255 return; // ignore... - it's a 'hash' to another page.
6257 Roo.log("NavItem - prevent Default?");
6259 this.scrollToElement(e);
6263 var p = this.parent();
6265 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266 if (typeof(p.setActiveItem) !== 'undefined') {
6267 p.setActiveItem(this);
6271 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273 // remove the collapsed menu expand...
6274 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6278 isActive: function () {
6281 setActive : function(state, fire, is_was_active)
6283 if (this.active && !state && this.navId) {
6284 this.was_active = true;
6285 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6287 nv.clearWasActive(this);
6291 this.active = state;
6294 this.el.removeClass('active');
6295 this.navLink ? this.navLink.removeClass('active') : false;
6296 } else if (!this.el.hasClass('active')) {
6298 this.el.addClass('active');
6299 if (Roo.bootstrap.version == 4 && this.navLink ) {
6300 this.navLink.addClass('active');
6305 this.fireEvent('changed', this, state);
6308 // show a panel if it's registered and related..
6310 if (!this.navId || !this.tabId || !state || is_was_active) {
6314 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6318 var pan = tg.getPanelByName(this.tabId);
6322 // if we can not flip to new panel - go back to old nav highlight..
6323 if (false == tg.showPanel(pan)) {
6324 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6326 var onav = nv.getWasActive();
6328 onav.setActive(true, false, true);
6337 // this should not be here...
6338 setDisabled : function(state)
6340 this.disabled = state;
6342 this.el.removeClass('disabled');
6343 } else if (!this.el.hasClass('disabled')) {
6344 this.el.addClass('disabled');
6350 * Fetch the element to display the tooltip on.
6351 * @return {Roo.Element} defaults to this.el
6353 tooltipEl : function()
6355 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6358 scrollToElement : function(e)
6360 var c = document.body;
6363 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6365 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366 c = document.documentElement;
6369 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6375 var o = target.calcOffsetsTo(c);
6382 this.fireEvent('scrollto', this, options, e);
6384 Roo.get(c).scrollTo('top', options.value, true);
6397 * <span> icon </span>
6398 * <span> text </span>
6399 * <span>badge </span>
6403 * @class Roo.bootstrap.NavSidebarItem
6404 * @extends Roo.bootstrap.NavItem
6405 * Bootstrap Navbar.NavSidebarItem class
6406 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407 * {Boolean} open is the menu open
6408 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410 * {String} buttonSize (sm|md|lg)the extra classes for the button
6411 * {Boolean} showArrow show arrow next to the text (default true)
6413 * Create a new Navbar Button
6414 * @param {Object} config The config object
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6422 * The raw click event for the entire grid.
6423 * @param {Roo.EventObject} e
6428 * Fires when the active item active state changes
6429 * @param {Roo.bootstrap.NavSidebarItem} this
6430 * @param {boolean} state the new state
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6440 badgeWeight : 'default',
6446 buttonWeight : 'default',
6452 getAutoCreate : function(){
6457 href : this.href || '#',
6463 if(this.buttonView){
6466 href : this.href || '#',
6467 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6480 cfg.cls += ' active';
6483 if (this.disabled) {
6484 cfg.cls += ' disabled';
6487 cfg.cls += ' open x-open';
6490 if (this.glyphicon || this.icon) {
6491 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6492 a.cn.push({ tag : 'i', cls : c }) ;
6495 if(!this.buttonView){
6498 html : this.html || ''
6505 if (this.badge !== '') {
6506 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6512 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6515 a.cls += ' dropdown-toggle treeview' ;
6521 initEvents : function()
6523 if (typeof (this.menu) != 'undefined') {
6524 this.menu.parentType = this.xtype;
6525 this.menu.triggerEl = this.el;
6526 this.menu = this.addxtype(Roo.apply({}, this.menu));
6529 this.el.on('click', this.onClick, this);
6531 if(this.badge !== ''){
6532 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6537 onClick : function(e)
6544 if(this.preventDefault){
6548 this.fireEvent('click', this, e);
6551 disable : function()
6553 this.setDisabled(true);
6558 this.setDisabled(false);
6561 setDisabled : function(state)
6563 if(this.disabled == state){
6567 this.disabled = state;
6570 this.el.addClass('disabled');
6574 this.el.removeClass('disabled');
6579 setActive : function(state)
6581 if(this.active == state){
6585 this.active = state;
6588 this.el.addClass('active');
6592 this.el.removeClass('active');
6597 isActive: function ()
6602 setBadge : function(str)
6608 this.badgeEl.dom.innerHTML = str;
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6627 * @class Roo.bootstrap.breadcrumb.Nav
6628 * @extends Roo.bootstrap.Component
6629 * Bootstrap Breadcrumb Nav Class
6631 * @children Roo.bootstrap.breadcrumb.Item
6634 * Create a new breadcrumb.Nav
6635 * @param {Object} config The config object
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6647 getAutoCreate : function()
6664 initEvents: function()
6666 this.olEl = this.el.select('ol',true).first();
6668 getChildContainer : function()
6684 * @class Roo.bootstrap.breadcrumb.Nav
6685 * @extends Roo.bootstrap.Component
6686 * Bootstrap Breadcrumb Nav Class
6688 * @children Roo.bootstrap.breadcrumb.Component
6689 * @cfg {String} html the content of the link.
6690 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691 * @cfg {Boolean} active is it active
6695 * Create a new breadcrumb.Nav
6696 * @param {Object} config The config object
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6705 * The img click event for the img.
6706 * @param {Roo.EventObject} e
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6718 getAutoCreate : function()
6723 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6725 if (this.href !== false) {
6732 cfg.html = this.html;
6738 initEvents: function()
6741 this.el.select('a', true).first().on('click',this.onClick, this)
6745 onClick : function(e)
6748 this.fireEvent('click',this, e);
6761 * @class Roo.bootstrap.Row
6762 * @extends Roo.bootstrap.Component
6763 * Bootstrap Row class (contains columns...)
6767 * @param {Object} config The config object
6770 Roo.bootstrap.Row = function(config){
6771 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6776 getAutoCreate : function(){
6795 * @class Roo.bootstrap.Pagination
6796 * @extends Roo.bootstrap.Component
6797 * Bootstrap Pagination class
6798 * @cfg {String} size xs | sm | md | lg
6799 * @cfg {Boolean} inverse false | true
6802 * Create a new Pagination
6803 * @param {Object} config The config object
6806 Roo.bootstrap.Pagination = function(config){
6807 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6816 getAutoCreate : function(){
6822 cfg.cls += ' inverse';
6828 cfg.cls += " " + this.cls;
6846 * @class Roo.bootstrap.PaginationItem
6847 * @extends Roo.bootstrap.Component
6848 * Bootstrap PaginationItem class
6849 * @cfg {String} html text
6850 * @cfg {String} href the link
6851 * @cfg {Boolean} preventDefault (true | false) default true
6852 * @cfg {Boolean} active (true | false) default false
6853 * @cfg {Boolean} disabled default false
6857 * Create a new PaginationItem
6858 * @param {Object} config The config object
6862 Roo.bootstrap.PaginationItem = function(config){
6863 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6868 * The raw click event for the entire grid.
6869 * @param {Roo.EventObject} e
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6879 preventDefault: true,
6884 getAutoCreate : function(){
6890 href : this.href ? this.href : '#',
6891 html : this.html ? this.html : ''
6901 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6905 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6911 initEvents: function() {
6913 this.el.on('click', this.onClick, this);
6916 onClick : function(e)
6918 Roo.log('PaginationItem on click ');
6919 if(this.preventDefault){
6927 this.fireEvent('click', this, e);
6943 * @class Roo.bootstrap.Slider
6944 * @extends Roo.bootstrap.Component
6945 * Bootstrap Slider class
6948 * Create a new Slider
6949 * @param {Object} config The config object
6952 Roo.bootstrap.Slider = function(config){
6953 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6958 getAutoCreate : function(){
6962 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6966 cls: 'ui-slider-handle ui-state-default ui-corner-all'
6978 * Ext JS Library 1.1.1
6979 * Copyright(c) 2006-2007, Ext JS, LLC.
6981 * Originally Released Under LGPL - original licence link has changed is not relivant.
6984 * <script type="text/javascript">
6989 * @class Roo.grid.ColumnModel
6990 * @extends Roo.util.Observable
6991 * This is the default implementation of a ColumnModel used by the Grid. It defines
6992 * the columns in the grid.
6995 var colModel = new Roo.grid.ColumnModel([
6996 {header: "Ticker", width: 60, sortable: true, locked: true},
6997 {header: "Company Name", width: 150, sortable: true},
6998 {header: "Market Cap.", width: 100, sortable: true},
6999 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000 {header: "Employees", width: 100, sortable: true, resizable: false}
7005 * The config options listed for this class are options which may appear in each
7006 * individual column definition.
7007 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7009 * @param {Object} config An Array of column config objects. See this class's
7010 * config objects for details.
7012 Roo.grid.ColumnModel = function(config){
7014 * The config passed into the constructor
7016 this.config = config;
7019 // if no id, create one
7020 // if the column does not have a dataIndex mapping,
7021 // map it to the order it is in the config
7022 for(var i = 0, len = config.length; i < len; i++){
7024 if(typeof c.dataIndex == "undefined"){
7027 if(typeof c.renderer == "string"){
7028 c.renderer = Roo.util.Format[c.renderer];
7030 if(typeof c.id == "undefined"){
7033 if(c.editor && c.editor.xtype){
7034 c.editor = Roo.factory(c.editor, Roo.grid);
7036 if(c.editor && c.editor.isFormField){
7037 c.editor = new Roo.grid.GridEditor(c.editor);
7039 this.lookup[c.id] = c;
7043 * The width of columns which have no width specified (defaults to 100)
7046 this.defaultWidth = 100;
7049 * Default sortable of columns which have no sortable specified (defaults to false)
7052 this.defaultSortable = false;
7056 * @event widthchange
7057 * Fires when the width of a column changes.
7058 * @param {ColumnModel} this
7059 * @param {Number} columnIndex The column index
7060 * @param {Number} newWidth The new width
7062 "widthchange": true,
7064 * @event headerchange
7065 * Fires when the text of a header changes.
7066 * @param {ColumnModel} this
7067 * @param {Number} columnIndex The column index
7068 * @param {Number} newText The new header text
7070 "headerchange": true,
7072 * @event hiddenchange
7073 * Fires when a column is hidden or "unhidden".
7074 * @param {ColumnModel} this
7075 * @param {Number} columnIndex The column index
7076 * @param {Boolean} hidden true if hidden, false otherwise
7078 "hiddenchange": true,
7080 * @event columnmoved
7081 * Fires when a column is moved.
7082 * @param {ColumnModel} this
7083 * @param {Number} oldIndex
7084 * @param {Number} newIndex
7086 "columnmoved" : true,
7088 * @event columlockchange
7089 * Fires when a column's locked state is changed
7090 * @param {ColumnModel} this
7091 * @param {Number} colIndex
7092 * @param {Boolean} locked true if locked
7094 "columnlockchange" : true
7096 Roo.grid.ColumnModel.superclass.constructor.call(this);
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7100 * @cfg {String} header The header text to display in the Grid view.
7103 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105 * specified, the column's index is used as an index into the Record's data Array.
7108 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7112 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113 * Defaults to the value of the {@link #defaultSortable} property.
7114 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7117 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7120 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7123 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7126 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7129 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7135 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7138 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7141 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7144 * @cfg {String} cursor (Optional)
7147 * @cfg {String} tooltip (Optional)
7150 * @cfg {Number} xs (Optional)
7153 * @cfg {Number} sm (Optional)
7156 * @cfg {Number} md (Optional)
7159 * @cfg {Number} lg (Optional)
7162 * Returns the id of the column at the specified index.
7163 * @param {Number} index The column index
7164 * @return {String} the id
7166 getColumnId : function(index){
7167 return this.config[index].id;
7171 * Returns the column for a specified id.
7172 * @param {String} id The column id
7173 * @return {Object} the column
7175 getColumnById : function(id){
7176 return this.lookup[id];
7181 * Returns the column for a specified dataIndex.
7182 * @param {String} dataIndex The column dataIndex
7183 * @return {Object|Boolean} the column or false if not found
7185 getColumnByDataIndex: function(dataIndex){
7186 var index = this.findColumnIndex(dataIndex);
7187 return index > -1 ? this.config[index] : false;
7191 * Returns the index for a specified column id.
7192 * @param {String} id The column id
7193 * @return {Number} the index, or -1 if not found
7195 getIndexById : function(id){
7196 for(var i = 0, len = this.config.length; i < len; i++){
7197 if(this.config[i].id == id){
7205 * Returns the index for a specified column dataIndex.
7206 * @param {String} dataIndex The column dataIndex
7207 * @return {Number} the index, or -1 if not found
7210 findColumnIndex : function(dataIndex){
7211 for(var i = 0, len = this.config.length; i < len; i++){
7212 if(this.config[i].dataIndex == dataIndex){
7220 moveColumn : function(oldIndex, newIndex){
7221 var c = this.config[oldIndex];
7222 this.config.splice(oldIndex, 1);
7223 this.config.splice(newIndex, 0, c);
7224 this.dataMap = null;
7225 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7228 isLocked : function(colIndex){
7229 return this.config[colIndex].locked === true;
7232 setLocked : function(colIndex, value, suppressEvent){
7233 if(this.isLocked(colIndex) == value){
7236 this.config[colIndex].locked = value;
7238 this.fireEvent("columnlockchange", this, colIndex, value);
7242 getTotalLockedWidth : function(){
7244 for(var i = 0; i < this.config.length; i++){
7245 if(this.isLocked(i) && !this.isHidden(i)){
7246 this.totalWidth += this.getColumnWidth(i);
7252 getLockedCount : function(){
7253 for(var i = 0, len = this.config.length; i < len; i++){
7254 if(!this.isLocked(i)){
7259 return this.config.length;
7263 * Returns the number of columns.
7266 getColumnCount : function(visibleOnly){
7267 if(visibleOnly === true){
7269 for(var i = 0, len = this.config.length; i < len; i++){
7270 if(!this.isHidden(i)){
7276 return this.config.length;
7280 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281 * @param {Function} fn
7282 * @param {Object} scope (optional)
7283 * @return {Array} result
7285 getColumnsBy : function(fn, scope){
7287 for(var i = 0, len = this.config.length; i < len; i++){
7288 var c = this.config[i];
7289 if(fn.call(scope||this, c, i) === true){
7297 * Returns true if the specified column is sortable.
7298 * @param {Number} col The column index
7301 isSortable : function(col){
7302 if(typeof this.config[col].sortable == "undefined"){
7303 return this.defaultSortable;
7305 return this.config[col].sortable;
7309 * Returns the rendering (formatting) function defined for the column.
7310 * @param {Number} col The column index.
7311 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7313 getRenderer : function(col){
7314 if(!this.config[col].renderer){
7315 return Roo.grid.ColumnModel.defaultRenderer;
7317 return this.config[col].renderer;
7321 * Sets the rendering (formatting) function for a column.
7322 * @param {Number} col The column index
7323 * @param {Function} fn The function to use to process the cell's raw data
7324 * to return HTML markup for the grid view. The render function is called with
7325 * the following parameters:<ul>
7326 * <li>Data value.</li>
7327 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328 * <li>css A CSS style string to apply to the table cell.</li>
7329 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331 * <li>Row index</li>
7332 * <li>Column index</li>
7333 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7335 setRenderer : function(col, fn){
7336 this.config[col].renderer = fn;
7340 * Returns the width for the specified column.
7341 * @param {Number} col The column index
7344 getColumnWidth : function(col){
7345 return this.config[col].width * 1 || this.defaultWidth;
7349 * Sets the width for a column.
7350 * @param {Number} col The column index
7351 * @param {Number} width The new width
7353 setColumnWidth : function(col, width, suppressEvent){
7354 this.config[col].width = width;
7355 this.totalWidth = null;
7357 this.fireEvent("widthchange", this, col, width);
7362 * Returns the total width of all columns.
7363 * @param {Boolean} includeHidden True to include hidden column widths
7366 getTotalWidth : function(includeHidden){
7367 if(!this.totalWidth){
7368 this.totalWidth = 0;
7369 for(var i = 0, len = this.config.length; i < len; i++){
7370 if(includeHidden || !this.isHidden(i)){
7371 this.totalWidth += this.getColumnWidth(i);
7375 return this.totalWidth;
7379 * Returns the header for the specified column.
7380 * @param {Number} col The column index
7383 getColumnHeader : function(col){
7384 return this.config[col].header;
7388 * Sets the header for a column.
7389 * @param {Number} col The column index
7390 * @param {String} header The new header
7392 setColumnHeader : function(col, header){
7393 this.config[col].header = header;
7394 this.fireEvent("headerchange", this, col, header);
7398 * Returns the tooltip for the specified column.
7399 * @param {Number} col The column index
7402 getColumnTooltip : function(col){
7403 return this.config[col].tooltip;
7406 * Sets the tooltip for a column.
7407 * @param {Number} col The column index
7408 * @param {String} tooltip The new tooltip
7410 setColumnTooltip : function(col, tooltip){
7411 this.config[col].tooltip = tooltip;
7415 * Returns the dataIndex for the specified column.
7416 * @param {Number} col The column index
7419 getDataIndex : function(col){
7420 return this.config[col].dataIndex;
7424 * Sets the dataIndex for a column.
7425 * @param {Number} col The column index
7426 * @param {Number} dataIndex The new dataIndex
7428 setDataIndex : function(col, dataIndex){
7429 this.config[col].dataIndex = dataIndex;
7435 * Returns true if the cell is editable.
7436 * @param {Number} colIndex The column index
7437 * @param {Number} rowIndex The row index - this is nto actually used..?
7440 isCellEditable : function(colIndex, rowIndex){
7441 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7445 * Returns the editor defined for the cell/column.
7446 * return false or null to disable editing.
7447 * @param {Number} colIndex The column index
7448 * @param {Number} rowIndex The row index
7451 getCellEditor : function(colIndex, rowIndex){
7452 return this.config[colIndex].editor;
7456 * Sets if a column is editable.
7457 * @param {Number} col The column index
7458 * @param {Boolean} editable True if the column is editable
7460 setEditable : function(col, editable){
7461 this.config[col].editable = editable;
7466 * Returns true if the column is hidden.
7467 * @param {Number} colIndex The column index
7470 isHidden : function(colIndex){
7471 return this.config[colIndex].hidden;
7476 * Returns true if the column width cannot be changed
7478 isFixed : function(colIndex){
7479 return this.config[colIndex].fixed;
7483 * Returns true if the column can be resized
7486 isResizable : function(colIndex){
7487 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7490 * Sets if a column is hidden.
7491 * @param {Number} colIndex The column index
7492 * @param {Boolean} hidden True if the column is hidden
7494 setHidden : function(colIndex, hidden){
7495 this.config[colIndex].hidden = hidden;
7496 this.totalWidth = null;
7497 this.fireEvent("hiddenchange", this, colIndex, hidden);
7501 * Sets the editor for a column.
7502 * @param {Number} col The column index
7503 * @param {Object} editor The editor object
7505 setEditor : function(col, editor){
7506 this.config[col].editor = editor;
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7512 if(typeof value == "object") {
7515 if(typeof value == "string" && value.length < 1){
7519 return String.format("{0}", value);
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7526 * Ext JS Library 1.1.1
7527 * Copyright(c) 2006-2007, Ext JS, LLC.
7529 * Originally Released Under LGPL - original licence link has changed is not relivant.
7532 * <script type="text/javascript">
7536 * @class Roo.LoadMask
7537 * A simple utility class for generically masking elements while loading data. If the element being masked has
7538 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7540 * element's UpdateManager load indicator and will be destroyed after the initial load.
7542 * Create a new LoadMask
7543 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544 * @param {Object} config The config object
7546 Roo.LoadMask = function(el, config){
7547 this.el = Roo.get(el);
7548 Roo.apply(this, config);
7550 this.store.on('beforeload', this.onBeforeLoad, this);
7551 this.store.on('load', this.onLoad, this);
7552 this.store.on('loadexception', this.onLoadException, this);
7553 this.removeMask = false;
7555 var um = this.el.getUpdateManager();
7556 um.showLoadIndicator = false; // disable the default indicator
7557 um.on('beforeupdate', this.onBeforeLoad, this);
7558 um.on('update', this.onLoad, this);
7559 um.on('failure', this.onLoad, this);
7560 this.removeMask = true;
7564 Roo.LoadMask.prototype = {
7566 * @cfg {Boolean} removeMask
7567 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7572 * The text to display in a centered loading message box (defaults to 'Loading...')
7576 * @cfg {String} msgCls
7577 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7579 msgCls : 'x-mask-loading',
7582 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7588 * Disables the mask to prevent it from being displayed
7590 disable : function(){
7591 this.disabled = true;
7595 * Enables the mask so that it can be displayed
7597 enable : function(){
7598 this.disabled = false;
7601 onLoadException : function()
7605 if (typeof(arguments[3]) != 'undefined') {
7606 Roo.MessageBox.alert("Error loading",arguments[3]);
7610 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7618 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7623 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7627 onBeforeLoad : function(){
7629 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7634 destroy : function(){
7636 this.store.un('beforeload', this.onBeforeLoad, this);
7637 this.store.un('load', this.onLoad, this);
7638 this.store.un('loadexception', this.onLoadException, this);
7640 var um = this.el.getUpdateManager();
7641 um.un('beforeupdate', this.onBeforeLoad, this);
7642 um.un('update', this.onLoad, this);
7643 um.un('failure', this.onLoad, this);
7654 * @class Roo.bootstrap.Table
7655 * @extends Roo.bootstrap.Component
7656 * Bootstrap Table class
7657 * @cfg {String} cls table class
7658 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659 * @cfg {String} bgcolor Specifies the background color for a table
7660 * @cfg {Number} border Specifies whether the table cells should have borders or not
7661 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662 * @cfg {Number} cellspacing Specifies the space between cells
7663 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665 * @cfg {String} sortable Specifies that the table should be sortable
7666 * @cfg {String} summary Specifies a summary of the content of a table
7667 * @cfg {Number} width Specifies the width of a table
7668 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7670 * @cfg {boolean} striped Should the rows be alternative striped
7671 * @cfg {boolean} bordered Add borders to the table
7672 * @cfg {boolean} hover Add hover highlighting
7673 * @cfg {boolean} condensed Format condensed
7674 * @cfg {boolean} responsive Format condensed
7675 * @cfg {Boolean} loadMask (true|false) default false
7676 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678 * @cfg {Boolean} rowSelection (true|false) default false
7679 * @cfg {Boolean} cellSelection (true|false) default false
7680 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7682 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7683 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7687 * Create a new Table
7688 * @param {Object} config The config object
7691 Roo.bootstrap.Table = function(config){
7692 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7697 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7702 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7704 this.sm.grid = this;
7705 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706 this.sm = this.selModel;
7707 this.sm.xmodule = this.xmodule || false;
7710 if (this.cm && typeof(this.cm.config) == 'undefined') {
7711 this.colModel = new Roo.grid.ColumnModel(this.cm);
7712 this.cm = this.colModel;
7713 this.cm.xmodule = this.xmodule || false;
7716 this.store= Roo.factory(this.store, Roo.data);
7717 this.ds = this.store;
7718 this.ds.xmodule = this.xmodule || false;
7721 if (this.footer && this.store) {
7722 this.footer.dataSource = this.ds;
7723 this.footer = Roo.factory(this.footer);
7730 * Fires when a cell is clicked
7731 * @param {Roo.bootstrap.Table} this
7732 * @param {Roo.Element} el
7733 * @param {Number} rowIndex
7734 * @param {Number} columnIndex
7735 * @param {Roo.EventObject} e
7739 * @event celldblclick
7740 * Fires when a cell is double clicked
7741 * @param {Roo.bootstrap.Table} this
7742 * @param {Roo.Element} el
7743 * @param {Number} rowIndex
7744 * @param {Number} columnIndex
7745 * @param {Roo.EventObject} e
7747 "celldblclick" : true,
7750 * Fires when a row is clicked
7751 * @param {Roo.bootstrap.Table} this
7752 * @param {Roo.Element} el
7753 * @param {Number} rowIndex
7754 * @param {Roo.EventObject} e
7758 * @event rowdblclick
7759 * Fires when a row is double clicked
7760 * @param {Roo.bootstrap.Table} this
7761 * @param {Roo.Element} el
7762 * @param {Number} rowIndex
7763 * @param {Roo.EventObject} e
7765 "rowdblclick" : true,
7768 * Fires when a mouseover occur
7769 * @param {Roo.bootstrap.Table} this
7770 * @param {Roo.Element} el
7771 * @param {Number} rowIndex
7772 * @param {Number} columnIndex
7773 * @param {Roo.EventObject} e
7778 * Fires when a mouseout occur
7779 * @param {Roo.bootstrap.Table} this
7780 * @param {Roo.Element} el
7781 * @param {Number} rowIndex
7782 * @param {Number} columnIndex
7783 * @param {Roo.EventObject} e
7788 * Fires when a row is rendered, so you can change add a style to it.
7789 * @param {Roo.bootstrap.Table} this
7790 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7794 * @event rowsrendered
7795 * Fires when all the rows have been rendered
7796 * @param {Roo.bootstrap.Table} this
7798 'rowsrendered' : true,
7800 * @event contextmenu
7801 * The raw contextmenu event for the entire grid.
7802 * @param {Roo.EventObject} e
7804 "contextmenu" : true,
7806 * @event rowcontextmenu
7807 * Fires when a row is right clicked
7808 * @param {Roo.bootstrap.Table} this
7809 * @param {Number} rowIndex
7810 * @param {Roo.EventObject} e
7812 "rowcontextmenu" : true,
7814 * @event cellcontextmenu
7815 * Fires when a cell is right clicked
7816 * @param {Roo.bootstrap.Table} this
7817 * @param {Number} rowIndex
7818 * @param {Number} cellIndex
7819 * @param {Roo.EventObject} e
7821 "cellcontextmenu" : true,
7823 * @event headercontextmenu
7824 * Fires when a header is right clicked
7825 * @param {Roo.bootstrap.Table} this
7826 * @param {Number} columnIndex
7827 * @param {Roo.EventObject} e
7829 "headercontextmenu" : true
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7859 rowSelection : false,
7860 cellSelection : false,
7863 // Roo.Element - the tbody
7865 // Roo.Element - thead element
7868 container: false, // used by gridpanel...
7874 auto_hide_footer : false,
7876 getAutoCreate : function()
7878 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7885 if (this.scrollBody) {
7886 cfg.cls += ' table-body-fixed';
7889 cfg.cls += ' table-striped';
7893 cfg.cls += ' table-hover';
7895 if (this.bordered) {
7896 cfg.cls += ' table-bordered';
7898 if (this.condensed) {
7899 cfg.cls += ' table-condensed';
7901 if (this.responsive) {
7902 cfg.cls += ' table-responsive';
7906 cfg.cls+= ' ' +this.cls;
7909 // this lot should be simplifed...
7922 ].forEach(function(k) {
7930 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7933 if(this.store || this.cm){
7934 if(this.headerShow){
7935 cfg.cn.push(this.renderHeader());
7938 cfg.cn.push(this.renderBody());
7940 if(this.footerShow){
7941 cfg.cn.push(this.renderFooter());
7943 // where does this come from?
7944 //cfg.cls+= ' TableGrid';
7947 return { cn : [ cfg ] };
7950 initEvents : function()
7952 if(!this.store || !this.cm){
7955 if (this.selModel) {
7956 this.selModel.initEvents();
7960 //Roo.log('initEvents with ds!!!!');
7962 this.mainBody = this.el.select('tbody', true).first();
7963 this.mainHead = this.el.select('thead', true).first();
7964 this.mainFoot = this.el.select('tfoot', true).first();
7970 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971 e.on('click', _this.sort, _this);
7974 this.mainBody.on("click", this.onClick, this);
7975 this.mainBody.on("dblclick", this.onDblClick, this);
7977 // why is this done????? = it breaks dialogs??
7978 //this.parent().el.setStyle('position', 'relative');
7982 this.footer.parentId = this.id;
7983 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7986 this.el.select('tfoot tr td').first().addClass('hide');
7991 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7994 this.store.on('load', this.onLoad, this);
7995 this.store.on('beforeload', this.onBeforeLoad, this);
7996 this.store.on('update', this.onUpdate, this);
7997 this.store.on('add', this.onAdd, this);
7998 this.store.on("clear", this.clear, this);
8000 this.el.on("contextmenu", this.onContextMenu, this);
8002 this.mainBody.on('scroll', this.onBodyScroll, this);
8004 this.cm.on("headerchange", this.onHeaderChange, this);
8006 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8010 onContextMenu : function(e, t)
8012 this.processEvent("contextmenu", e);
8015 processEvent : function(name, e)
8017 if (name != 'touchstart' ) {
8018 this.fireEvent(name, e);
8021 var t = e.getTarget();
8023 var cell = Roo.get(t);
8029 if(cell.findParent('tfoot', false, true)){
8033 if(cell.findParent('thead', false, true)){
8035 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036 cell = Roo.get(t).findParent('th', false, true);
8038 Roo.log("failed to find th in thead?");
8039 Roo.log(e.getTarget());
8044 var cellIndex = cell.dom.cellIndex;
8046 var ename = name == 'touchstart' ? 'click' : name;
8047 this.fireEvent("header" + ename, this, cellIndex, e);
8052 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053 cell = Roo.get(t).findParent('td', false, true);
8055 Roo.log("failed to find th in tbody?");
8056 Roo.log(e.getTarget());
8061 var row = cell.findParent('tr', false, true);
8062 var cellIndex = cell.dom.cellIndex;
8063 var rowIndex = row.dom.rowIndex - 1;
8067 this.fireEvent("row" + name, this, rowIndex, e);
8071 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8077 onMouseover : function(e, el)
8079 var cell = Roo.get(el);
8085 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086 cell = cell.findParent('td', false, true);
8089 var row = cell.findParent('tr', false, true);
8090 var cellIndex = cell.dom.cellIndex;
8091 var rowIndex = row.dom.rowIndex - 1; // start from 0
8093 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8097 onMouseout : function(e, el)
8099 var cell = Roo.get(el);
8105 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106 cell = cell.findParent('td', false, true);
8109 var row = cell.findParent('tr', false, true);
8110 var cellIndex = cell.dom.cellIndex;
8111 var rowIndex = row.dom.rowIndex - 1; // start from 0
8113 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8117 onClick : function(e, el)
8119 var cell = Roo.get(el);
8121 if(!cell || (!this.cellSelection && !this.rowSelection)){
8125 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126 cell = cell.findParent('td', false, true);
8129 if(!cell || typeof(cell) == 'undefined'){
8133 var row = cell.findParent('tr', false, true);
8135 if(!row || typeof(row) == 'undefined'){
8139 var cellIndex = cell.dom.cellIndex;
8140 var rowIndex = this.getRowIndex(row);
8142 // why??? - should these not be based on SelectionModel?
8143 if(this.cellSelection){
8144 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8147 if(this.rowSelection){
8148 this.fireEvent('rowclick', this, row, rowIndex, e);
8154 onDblClick : function(e,el)
8156 var cell = Roo.get(el);
8158 if(!cell || (!this.cellSelection && !this.rowSelection)){
8162 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163 cell = cell.findParent('td', false, true);
8166 if(!cell || typeof(cell) == 'undefined'){
8170 var row = cell.findParent('tr', false, true);
8172 if(!row || typeof(row) == 'undefined'){
8176 var cellIndex = cell.dom.cellIndex;
8177 var rowIndex = this.getRowIndex(row);
8179 if(this.cellSelection){
8180 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8183 if(this.rowSelection){
8184 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8188 sort : function(e,el)
8190 var col = Roo.get(el);
8192 if(!col.hasClass('sortable')){
8196 var sort = col.attr('sort');
8199 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8203 this.store.sortInfo = {field : sort, direction : dir};
8206 Roo.log("calling footer first");
8207 this.footer.onClick('first');
8210 this.store.load({ params : { start : 0 } });
8214 renderHeader : function()
8222 this.totalWidth = 0;
8224 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8226 var config = cm.config[i];
8230 cls : 'x-hcol-' + i,
8232 html: cm.getColumnHeader(i)
8237 if(typeof(config.sortable) != 'undefined' && config.sortable){
8239 c.html = '<i class="glyphicon"></i>' + c.html;
8242 // could use BS4 hidden-..-down
8244 if(typeof(config.lgHeader) != 'undefined'){
8245 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8248 if(typeof(config.mdHeader) != 'undefined'){
8249 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8252 if(typeof(config.smHeader) != 'undefined'){
8253 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8256 if(typeof(config.xsHeader) != 'undefined'){
8257 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8264 if(typeof(config.tooltip) != 'undefined'){
8265 c.tooltip = config.tooltip;
8268 if(typeof(config.colspan) != 'undefined'){
8269 c.colspan = config.colspan;
8272 if(typeof(config.hidden) != 'undefined' && config.hidden){
8273 c.style += ' display:none;';
8276 if(typeof(config.dataIndex) != 'undefined'){
8277 c.sort = config.dataIndex;
8282 if(typeof(config.align) != 'undefined' && config.align.length){
8283 c.style += ' text-align:' + config.align + ';';
8286 if(typeof(config.width) != 'undefined'){
8287 c.style += ' width:' + config.width + 'px;';
8288 this.totalWidth += config.width;
8290 this.totalWidth += 100; // assume minimum of 100 per column?
8293 if(typeof(config.cls) != 'undefined'){
8294 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8297 ['xs','sm','md','lg'].map(function(size){
8299 if(typeof(config[size]) == 'undefined'){
8303 if (!config[size]) { // 0 = hidden
8304 // BS 4 '0' is treated as hide that column and below.
8305 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8309 c.cls += ' col-' + size + '-' + config[size] + (
8310 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8322 renderBody : function()
8332 colspan : this.cm.getColumnCount()
8342 renderFooter : function()
8352 colspan : this.cm.getColumnCount()
8366 // Roo.log('ds onload');
8371 var ds = this.store;
8373 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375 if (_this.store.sortInfo) {
8377 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378 e.select('i', true).addClass(['glyphicon-arrow-up']);
8381 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382 e.select('i', true).addClass(['glyphicon-arrow-down']);
8387 var tbody = this.mainBody;
8389 if(ds.getCount() > 0){
8390 ds.data.each(function(d,rowIndex){
8391 var row = this.renderRow(cm, ds, rowIndex);
8393 tbody.createChild(row);
8397 if(row.cellObjects.length){
8398 Roo.each(row.cellObjects, function(r){
8399 _this.renderCellObject(r);
8406 var tfoot = this.el.select('tfoot', true).first();
8408 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8410 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8412 var total = this.ds.getTotalCount();
8414 if(this.footer.pageSize < total){
8415 this.mainFoot.show();
8419 Roo.each(this.el.select('tbody td', true).elements, function(e){
8420 e.on('mouseover', _this.onMouseover, _this);
8423 Roo.each(this.el.select('tbody td', true).elements, function(e){
8424 e.on('mouseout', _this.onMouseout, _this);
8426 this.fireEvent('rowsrendered', this);
8432 onUpdate : function(ds,record)
8434 this.refreshRow(record);
8438 onRemove : function(ds, record, index, isUpdate){
8439 if(isUpdate !== true){
8440 this.fireEvent("beforerowremoved", this, index, record);
8442 var bt = this.mainBody.dom;
8444 var rows = this.el.select('tbody > tr', true).elements;
8446 if(typeof(rows[index]) != 'undefined'){
8447 bt.removeChild(rows[index].dom);
8450 // if(bt.rows[index]){
8451 // bt.removeChild(bt.rows[index]);
8454 if(isUpdate !== true){
8455 //this.stripeRows(index);
8456 //this.syncRowHeights(index, index);
8458 this.fireEvent("rowremoved", this, index, record);
8462 onAdd : function(ds, records, rowIndex)
8464 //Roo.log('on Add called');
8465 // - note this does not handle multiple adding very well..
8466 var bt = this.mainBody.dom;
8467 for (var i =0 ; i < records.length;i++) {
8468 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469 //Roo.log(records[i]);
8470 //Roo.log(this.store.getAt(rowIndex+i));
8471 this.insertRow(this.store, rowIndex + i, false);
8478 refreshRow : function(record){
8479 var ds = this.store, index;
8480 if(typeof record == 'number'){
8482 record = ds.getAt(index);
8484 index = ds.indexOf(record);
8486 return; // should not happen - but seems to
8489 this.insertRow(ds, index, true);
8491 this.onRemove(ds, record, index+1, true);
8493 //this.syncRowHeights(index, index);
8495 this.fireEvent("rowupdated", this, index, record);
8498 insertRow : function(dm, rowIndex, isUpdate){
8501 this.fireEvent("beforerowsinserted", this, rowIndex);
8503 //var s = this.getScrollState();
8504 var row = this.renderRow(this.cm, this.store, rowIndex);
8505 // insert before rowIndex..
8506 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8510 if(row.cellObjects.length){
8511 Roo.each(row.cellObjects, function(r){
8512 _this.renderCellObject(r);
8517 this.fireEvent("rowsinserted", this, rowIndex);
8518 //this.syncRowHeights(firstRow, lastRow);
8519 //this.stripeRows(firstRow);
8526 getRowDom : function(rowIndex)
8528 var rows = this.el.select('tbody > tr', true).elements;
8530 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8533 // returns the object tree for a tr..
8536 renderRow : function(cm, ds, rowIndex)
8538 var d = ds.getAt(rowIndex);
8542 cls : 'x-row-' + rowIndex,
8546 var cellObjects = [];
8548 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549 var config = cm.config[i];
8551 var renderer = cm.getRenderer(i);
8555 if(typeof(renderer) !== 'undefined'){
8556 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8558 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559 // and are rendered into the cells after the row is rendered - using the id for the element.
8561 if(typeof(value) === 'object'){
8571 rowIndex : rowIndex,
8576 this.fireEvent('rowclass', this, rowcfg);
8580 cls : rowcfg.rowClass + ' x-col-' + i,
8582 html: (typeof(value) === 'object') ? '' : value
8589 if(typeof(config.colspan) != 'undefined'){
8590 td.colspan = config.colspan;
8593 if(typeof(config.hidden) != 'undefined' && config.hidden){
8594 td.style += ' display:none;';
8597 if(typeof(config.align) != 'undefined' && config.align.length){
8598 td.style += ' text-align:' + config.align + ';';
8600 if(typeof(config.valign) != 'undefined' && config.valign.length){
8601 td.style += ' vertical-align:' + config.valign + ';';
8604 if(typeof(config.width) != 'undefined'){
8605 td.style += ' width:' + config.width + 'px;';
8608 if(typeof(config.cursor) != 'undefined'){
8609 td.style += ' cursor:' + config.cursor + ';';
8612 if(typeof(config.cls) != 'undefined'){
8613 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8616 ['xs','sm','md','lg'].map(function(size){
8618 if(typeof(config[size]) == 'undefined'){
8624 if (!config[size]) { // 0 = hidden
8625 // BS 4 '0' is treated as hide that column and below.
8626 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8630 td.cls += ' col-' + size + '-' + config[size] + (
8631 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8641 row.cellObjects = cellObjects;
8649 onBeforeLoad : function()
8658 this.el.select('tbody', true).first().dom.innerHTML = '';
8661 * Show or hide a row.
8662 * @param {Number} rowIndex to show or hide
8663 * @param {Boolean} state hide
8665 setRowVisibility : function(rowIndex, state)
8667 var bt = this.mainBody.dom;
8669 var rows = this.el.select('tbody > tr', true).elements;
8671 if(typeof(rows[rowIndex]) == 'undefined'){
8674 rows[rowIndex].dom.style.display = state ? '' : 'none';
8678 getSelectionModel : function(){
8680 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8682 return this.selModel;
8685 * Render the Roo.bootstrap object from renderder
8687 renderCellObject : function(r)
8691 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8693 var t = r.cfg.render(r.container);
8696 Roo.each(r.cfg.cn, function(c){
8698 container: t.getChildContainer(),
8701 _this.renderCellObject(child);
8706 getRowIndex : function(row)
8710 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8721 * Returns the grid's underlying element = used by panel.Grid
8722 * @return {Element} The element
8724 getGridEl : function(){
8728 * Forces a resize - used by panel.Grid
8729 * @return {Element} The element
8731 autoSize : function()
8733 //var ctr = Roo.get(this.container.dom.parentElement);
8734 var ctr = Roo.get(this.el.dom);
8736 var thd = this.getGridEl().select('thead',true).first();
8737 var tbd = this.getGridEl().select('tbody', true).first();
8738 var tfd = this.getGridEl().select('tfoot', true).first();
8740 var cw = ctr.getWidth();
8741 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8745 tbd.setWidth(ctr.getWidth());
8746 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747 // this needs fixing for various usage - currently only hydra job advers I think..
8749 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8751 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8754 cw = Math.max(cw, this.totalWidth);
8755 this.getGridEl().select('tbody tr',true).setWidth(cw);
8757 // resize 'expandable coloumn?
8759 return; // we doe not have a view in this design..
8762 onBodyScroll: function()
8764 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8766 this.mainHead.setStyle({
8767 'position' : 'relative',
8768 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8774 var scrollHeight = this.mainBody.dom.scrollHeight;
8776 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8778 var height = this.mainBody.getHeight();
8780 if(scrollHeight - height == scrollTop) {
8782 var total = this.ds.getTotalCount();
8784 if(this.footer.cursor + this.footer.pageSize < total){
8786 this.footer.ds.load({
8788 start : this.footer.cursor + this.footer.pageSize,
8789 limit : this.footer.pageSize
8799 onHeaderChange : function()
8801 var header = this.renderHeader();
8802 var table = this.el.select('table', true).first();
8804 this.mainHead.remove();
8805 this.mainHead = table.createChild(header, this.mainBody, false);
8808 onHiddenChange : function(colModel, colIndex, hidden)
8810 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8813 this.CSS.updateRule(thSelector, "display", "");
8814 this.CSS.updateRule(tdSelector, "display", "");
8817 this.CSS.updateRule(thSelector, "display", "none");
8818 this.CSS.updateRule(tdSelector, "display", "none");
8821 this.onHeaderChange();
8825 setColumnWidth: function(col_index, width)
8827 // width = "md-2 xs-2..."
8828 if(!this.colModel.config[col_index]) {
8832 var w = width.split(" ");
8834 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8836 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8839 for(var j = 0; j < w.length; j++) {
8845 var size_cls = w[j].split("-");
8847 if(!Number.isInteger(size_cls[1] * 1)) {
8851 if(!this.colModel.config[col_index][size_cls[0]]) {
8855 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8859 h_row[0].classList.replace(
8860 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861 "col-"+size_cls[0]+"-"+size_cls[1]
8864 for(var i = 0; i < rows.length; i++) {
8866 var size_cls = w[j].split("-");
8868 if(!Number.isInteger(size_cls[1] * 1)) {
8872 if(!this.colModel.config[col_index][size_cls[0]]) {
8876 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8880 rows[i].classList.replace(
8881 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882 "col-"+size_cls[0]+"-"+size_cls[1]
8886 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8901 * @class Roo.bootstrap.TableCell
8902 * @extends Roo.bootstrap.Component
8903 * Bootstrap TableCell class
8904 * @cfg {String} html cell contain text
8905 * @cfg {String} cls cell class
8906 * @cfg {String} tag cell tag (td|th) default td
8907 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908 * @cfg {String} align Aligns the content in a cell
8909 * @cfg {String} axis Categorizes cells
8910 * @cfg {String} bgcolor Specifies the background color of a cell
8911 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912 * @cfg {Number} colspan Specifies the number of columns a cell should span
8913 * @cfg {String} headers Specifies one or more header cells a cell is related to
8914 * @cfg {Number} height Sets the height of a cell
8915 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916 * @cfg {Number} rowspan Sets the number of rows a cell should span
8917 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918 * @cfg {String} valign Vertical aligns the content in a cell
8919 * @cfg {Number} width Specifies the width of a cell
8922 * Create a new TableCell
8923 * @param {Object} config The config object
8926 Roo.bootstrap.TableCell = function(config){
8927 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8950 getAutoCreate : function(){
8951 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8971 cfg.align=this.align
8977 cfg.bgcolor=this.bgcolor
8980 cfg.charoff=this.charoff
8983 cfg.colspan=this.colspan
8986 cfg.headers=this.headers
8989 cfg.height=this.height
8992 cfg.nowrap=this.nowrap
8995 cfg.rowspan=this.rowspan
8998 cfg.scope=this.scope
9001 cfg.valign=this.valign
9004 cfg.width=this.width
9023 * @class Roo.bootstrap.TableRow
9024 * @extends Roo.bootstrap.Component
9025 * Bootstrap TableRow class
9026 * @cfg {String} cls row class
9027 * @cfg {String} align Aligns the content in a table row
9028 * @cfg {String} bgcolor Specifies a background color for a table row
9029 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030 * @cfg {String} valign Vertical aligns the content in a table row
9033 * Create a new TableRow
9034 * @param {Object} config The config object
9037 Roo.bootstrap.TableRow = function(config){
9038 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9049 getAutoCreate : function(){
9050 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9060 cfg.align = this.align;
9063 cfg.bgcolor = this.bgcolor;
9066 cfg.charoff = this.charoff;
9069 cfg.valign = this.valign;
9087 * @class Roo.bootstrap.TableBody
9088 * @extends Roo.bootstrap.Component
9089 * Bootstrap TableBody class
9090 * @cfg {String} cls element class
9091 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092 * @cfg {String} align Aligns the content inside the element
9093 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9097 * Create a new TableBody
9098 * @param {Object} config The config object
9101 Roo.bootstrap.TableBody = function(config){
9102 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9113 getAutoCreate : function(){
9114 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9128 cfg.align = this.align;
9131 cfg.charoff = this.charoff;
9134 cfg.valign = this.valign;
9141 // initEvents : function()
9148 // this.store = Roo.factory(this.store, Roo.data);
9149 // this.store.on('load', this.onLoad, this);
9151 // this.store.load();
9155 // onLoad: function ()
9157 // this.fireEvent('load', this);
9167 * Ext JS Library 1.1.1
9168 * Copyright(c) 2006-2007, Ext JS, LLC.
9170 * Originally Released Under LGPL - original licence link has changed is not relivant.
9173 * <script type="text/javascript">
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9179 * @class Roo.form.Action
9180 * Internal Class used to handle form actions
9182 * @param {Roo.form.BasicForm} el The form element or its id
9183 * @param {Object} config Configuration options
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9191 this.options = options || {};
9194 * Client Validation Failed
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9199 * Server Validation Failed
9202 Roo.form.Action.SERVER_INVALID = 'server';
9204 * Connect to Server Failed
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9209 * Reading Data from Server Failed
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9214 Roo.form.Action.prototype = {
9216 failureType : undefined,
9217 response : undefined,
9221 run : function(options){
9226 success : function(response){
9231 handleResponse : function(response){
9235 // default connection failure
9236 failure : function(response){
9238 this.response = response;
9239 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240 this.form.afterAction(this, false);
9243 processResponse : function(response){
9244 this.response = response;
9245 if(!response.responseText){
9248 this.result = this.handleResponse(response);
9252 // utility functions used internally
9253 getUrl : function(appendParams){
9254 var url = this.options.url || this.form.url || this.form.el.dom.action;
9256 var p = this.getParams();
9258 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9264 getMethod : function(){
9265 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9268 getParams : function(){
9269 var bp = this.form.baseParams;
9270 var p = this.options.params;
9272 if(typeof p == "object"){
9273 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274 }else if(typeof p == 'string' && bp){
9275 p += '&' + Roo.urlEncode(bp);
9278 p = Roo.urlEncode(bp);
9283 createCallback : function(){
9285 success: this.success,
9286 failure: this.failure,
9288 timeout: (this.form.timeout*1000),
9289 upload: this.form.fileUpload ? this.success : undefined
9294 Roo.form.Action.Submit = function(form, options){
9295 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9301 haveProgress : false,
9302 uploadComplete : false,
9304 // uploadProgress indicator.
9305 uploadProgress : function()
9307 if (!this.form.progressUrl) {
9311 if (!this.haveProgress) {
9312 Roo.MessageBox.progress("Uploading", "Uploading");
9314 if (this.uploadComplete) {
9315 Roo.MessageBox.hide();
9319 this.haveProgress = true;
9321 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9323 var c = new Roo.data.Connection();
9325 url : this.form.progressUrl,
9330 success : function(req){
9331 //console.log(data);
9335 rdata = Roo.decode(req.responseText)
9337 Roo.log("Invalid data from server..");
9341 if (!rdata || !rdata.success) {
9343 Roo.MessageBox.alert(Roo.encode(rdata));
9346 var data = rdata.data;
9348 if (this.uploadComplete) {
9349 Roo.MessageBox.hide();
9354 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9358 this.uploadProgress.defer(2000,this);
9361 failure: function(data) {
9362 Roo.log('progress url failed ');
9373 // run get Values on the form, so it syncs any secondary forms.
9374 this.form.getValues();
9376 var o = this.options;
9377 var method = this.getMethod();
9378 var isPost = method == 'POST';
9379 if(o.clientValidation === false || this.form.isValid()){
9381 if (this.form.progressUrl) {
9382 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383 (new Date() * 1) + '' + Math.random());
9388 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389 form:this.form.el.dom,
9390 url:this.getUrl(!isPost),
9392 params:isPost ? this.getParams() : null,
9393 isUpload: this.form.fileUpload,
9394 formData : this.form.formData
9397 this.uploadProgress();
9399 }else if (o.clientValidation !== false){ // client validation failed
9400 this.failureType = Roo.form.Action.CLIENT_INVALID;
9401 this.form.afterAction(this, false);
9405 success : function(response)
9407 this.uploadComplete= true;
9408 if (this.haveProgress) {
9409 Roo.MessageBox.hide();
9413 var result = this.processResponse(response);
9414 if(result === true || result.success){
9415 this.form.afterAction(this, true);
9419 this.form.markInvalid(result.errors);
9420 this.failureType = Roo.form.Action.SERVER_INVALID;
9422 this.form.afterAction(this, false);
9424 failure : function(response)
9426 this.uploadComplete= true;
9427 if (this.haveProgress) {
9428 Roo.MessageBox.hide();
9431 this.response = response;
9432 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433 this.form.afterAction(this, false);
9436 handleResponse : function(response){
9437 if(this.form.errorReader){
9438 var rs = this.form.errorReader.read(response);
9441 for(var i = 0, len = rs.records.length; i < len; i++) {
9442 var r = rs.records[i];
9446 if(errors.length < 1){
9450 success : rs.success,
9456 ret = Roo.decode(response.responseText);
9460 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9470 Roo.form.Action.Load = function(form, options){
9471 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472 this.reader = this.form.reader;
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9480 Roo.Ajax.request(Roo.apply(
9481 this.createCallback(), {
9482 method:this.getMethod(),
9483 url:this.getUrl(false),
9484 params:this.getParams()
9488 success : function(response){
9490 var result = this.processResponse(response);
9491 if(result === true || !result.success || !result.data){
9492 this.failureType = Roo.form.Action.LOAD_FAILURE;
9493 this.form.afterAction(this, false);
9496 this.form.clearInvalid();
9497 this.form.setValues(result.data);
9498 this.form.afterAction(this, true);
9501 handleResponse : function(response){
9502 if(this.form.reader){
9503 var rs = this.form.reader.read(response);
9504 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9506 success : rs.success,
9510 return Roo.decode(response.responseText);
9514 Roo.form.Action.ACTION_TYPES = {
9515 'load' : Roo.form.Action.Load,
9516 'submit' : Roo.form.Action.Submit
9525 * @class Roo.bootstrap.Form
9526 * @extends Roo.bootstrap.Component
9527 * Bootstrap Form class
9528 * @cfg {String} method GET | POST (default POST)
9529 * @cfg {String} labelAlign top | left (default top)
9530 * @cfg {String} align left | right - for navbars
9531 * @cfg {Boolean} loadMask load mask when submit (default true)
9536 * @param {Object} config The config object
9540 Roo.bootstrap.Form = function(config){
9542 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9544 Roo.bootstrap.Form.popover.apply();
9548 * @event clientvalidation
9549 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550 * @param {Form} this
9551 * @param {Boolean} valid true if the form has passed client-side validation
9553 clientvalidation: true,
9555 * @event beforeaction
9556 * Fires before any action is performed. Return false to cancel the action.
9557 * @param {Form} this
9558 * @param {Action} action The action to be performed
9562 * @event actionfailed
9563 * Fires when an action fails.
9564 * @param {Form} this
9565 * @param {Action} action The action that failed
9567 actionfailed : true,
9569 * @event actioncomplete
9570 * Fires when an action is completed.
9571 * @param {Form} this
9572 * @param {Action} action The action that completed
9574 actioncomplete : true
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9581 * @cfg {String} method
9582 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9587 * The URL to use for form actions if one isn't supplied in the action options.
9590 * @cfg {Boolean} fileUpload
9591 * Set to true if this form is a file upload.
9595 * @cfg {Object} baseParams
9596 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9600 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9604 * @cfg {Sting} align (left|right) for navbar forms
9609 activeAction : null,
9612 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613 * element by passing it or its id or mask the form itself by passing in true.
9616 waitMsgTarget : false,
9621 * @cfg {Boolean} errorMask (true|false) default false
9626 * @cfg {Number} maskOffset Default 100
9631 * @cfg {Boolean} maskBody
9635 getAutoCreate : function(){
9639 method : this.method || 'POST',
9640 id : this.id || Roo.id(),
9643 if (this.parent().xtype.match(/^Nav/)) {
9644 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9648 if (this.labelAlign == 'left' ) {
9649 cfg.cls += ' form-horizontal';
9655 initEvents : function()
9657 this.el.on('submit', this.onSubmit, this);
9658 // this was added as random key presses on the form where triggering form submit.
9659 this.el.on('keypress', function(e) {
9660 if (e.getCharCode() != 13) {
9663 // we might need to allow it for textareas.. and some other items.
9664 // check e.getTarget().
9666 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9670 Roo.log("keypress blocked");
9678 onSubmit : function(e){
9683 * Returns true if client-side validation on the form is successful.
9686 isValid : function(){
9687 var items = this.getItems();
9691 items.each(function(f){
9697 Roo.log('invalid field: ' + f.name);
9701 if(!target && f.el.isVisible(true)){
9707 if(this.errorMask && !valid){
9708 Roo.bootstrap.Form.popover.mask(this, target);
9715 * Returns true if any fields in this form have changed since their original load.
9718 isDirty : function(){
9720 var items = this.getItems();
9721 items.each(function(f){
9731 * Performs a predefined action (submit or load) or custom actions you define on this form.
9732 * @param {String} actionName The name of the action type
9733 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9734 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735 * accept other config options):
9737 Property Type Description
9738 ---------------- --------------- ----------------------------------------------------------------------------------
9739 url String The url for the action (defaults to the form's url)
9740 method String The form method to use (defaults to the form's method, or POST if not defined)
9741 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9743 validate the form on the client (defaults to false)
9745 * @return {BasicForm} this
9747 doAction : function(action, options){
9748 if(typeof action == 'string'){
9749 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9751 if(this.fireEvent('beforeaction', this, action) !== false){
9752 this.beforeAction(action);
9753 action.run.defer(100, action);
9759 beforeAction : function(action){
9760 var o = action.options;
9765 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9767 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9770 // not really supported yet.. ??
9772 //if(this.waitMsgTarget === true){
9773 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774 //}else if(this.waitMsgTarget){
9775 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9778 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9784 afterAction : function(action, success){
9785 this.activeAction = null;
9786 var o = action.options;
9791 Roo.get(document.body).unmask();
9797 //if(this.waitMsgTarget === true){
9798 // this.el.unmask();
9799 //}else if(this.waitMsgTarget){
9800 // this.waitMsgTarget.unmask();
9802 // Roo.MessageBox.updateProgress(1);
9803 // Roo.MessageBox.hide();
9810 Roo.callback(o.success, o.scope, [this, action]);
9811 this.fireEvent('actioncomplete', this, action);
9815 // failure condition..
9816 // we have a scenario where updates need confirming.
9817 // eg. if a locking scenario exists..
9818 // we look for { errors : { needs_confirm : true }} in the response.
9820 (typeof(action.result) != 'undefined') &&
9821 (typeof(action.result.errors) != 'undefined') &&
9822 (typeof(action.result.errors.needs_confirm) != 'undefined')
9825 Roo.log("not supported yet");
9828 Roo.MessageBox.confirm(
9829 "Change requires confirmation",
9830 action.result.errorMsg,
9835 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9845 Roo.callback(o.failure, o.scope, [this, action]);
9846 // show an error message if no failed handler is set..
9847 if (!this.hasListener('actionfailed')) {
9848 Roo.log("need to add dialog support");
9850 Roo.MessageBox.alert("Error",
9851 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852 action.result.errorMsg :
9853 "Saving Failed, please check your entries or try again"
9858 this.fireEvent('actionfailed', this, action);
9863 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864 * @param {String} id The value to search for
9867 findField : function(id){
9868 var items = this.getItems();
9869 var field = items.get(id);
9871 items.each(function(f){
9872 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9879 return field || null;
9882 * Mark fields in this form invalid in bulk.
9883 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884 * @return {BasicForm} this
9886 markInvalid : function(errors){
9887 if(errors instanceof Array){
9888 for(var i = 0, len = errors.length; i < len; i++){
9889 var fieldError = errors[i];
9890 var f = this.findField(fieldError.id);
9892 f.markInvalid(fieldError.msg);
9898 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899 field.markInvalid(errors[id]);
9903 //Roo.each(this.childForms || [], function (f) {
9904 // f.markInvalid(errors);
9911 * Set values for fields in this form in bulk.
9912 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913 * @return {BasicForm} this
9915 setValues : function(values){
9916 if(values instanceof Array){ // array of objects
9917 for(var i = 0, len = values.length; i < len; i++){
9919 var f = this.findField(v.id);
9921 f.setValue(v.value);
9922 if(this.trackResetOnLoad){
9923 f.originalValue = f.getValue();
9927 }else{ // object hash
9930 if(typeof values[id] != 'function' && (field = this.findField(id))){
9932 if (field.setFromData &&
9934 field.displayField &&
9935 // combos' with local stores can
9936 // be queried via setValue()
9937 // to set their value..
9938 (field.store && !field.store.isLocal)
9942 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944 field.setFromData(sd);
9946 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9948 field.setFromData(values);
9951 field.setValue(values[id]);
9955 if(this.trackResetOnLoad){
9956 field.originalValue = field.getValue();
9962 //Roo.each(this.childForms || [], function (f) {
9963 // f.setValues(values);
9970 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971 * they are returned as an array.
9972 * @param {Boolean} asString
9975 getValues : function(asString){
9976 //if (this.childForms) {
9977 // copy values from the child forms
9978 // Roo.each(this.childForms, function (f) {
9979 // this.setValues(f.getValues());
9985 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986 if(asString === true){
9989 return Roo.urlDecode(fs);
9993 * Returns the fields in this form as an object with key/value pairs.
9994 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9997 getFieldValues : function(with_hidden)
9999 var items = this.getItems();
10001 items.each(function(f){
10003 if (!f.getName()) {
10007 var v = f.getValue();
10009 if (f.inputType =='radio') {
10010 if (typeof(ret[f.getName()]) == 'undefined') {
10011 ret[f.getName()] = ''; // empty..
10014 if (!f.el.dom.checked) {
10018 v = f.el.dom.value;
10022 if(f.xtype == 'MoneyField'){
10023 ret[f.currencyName] = f.getCurrency();
10026 // not sure if this supported any more..
10027 if ((typeof(v) == 'object') && f.getRawValue) {
10028 v = f.getRawValue() ; // dates..
10030 // combo boxes where name != hiddenName...
10031 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032 ret[f.name] = f.getRawValue();
10034 ret[f.getName()] = v;
10041 * Clears all invalid messages in this form.
10042 * @return {BasicForm} this
10044 clearInvalid : function(){
10045 var items = this.getItems();
10047 items.each(function(f){
10055 * Resets this form.
10056 * @return {BasicForm} this
10058 reset : function(){
10059 var items = this.getItems();
10060 items.each(function(f){
10064 Roo.each(this.childForms || [], function (f) {
10072 getItems : function()
10074 var r=new Roo.util.MixedCollection(false, function(o){
10075 return o.id || (o.id = Roo.id());
10077 var iter = function(el) {
10084 Roo.each(el.items,function(e) {
10093 hideFields : function(items)
10095 Roo.each(items, function(i){
10097 var f = this.findField(i);
10108 showFields : function(items)
10110 Roo.each(items, function(i){
10112 var f = this.findField(i);
10125 Roo.apply(Roo.bootstrap.Form, {
10141 intervalID : false,
10147 if(this.isApplied){
10152 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10158 this.maskEl.top.enableDisplayMode("block");
10159 this.maskEl.left.enableDisplayMode("block");
10160 this.maskEl.bottom.enableDisplayMode("block");
10161 this.maskEl.right.enableDisplayMode("block");
10163 this.toolTip = new Roo.bootstrap.Tooltip({
10164 cls : 'roo-form-error-popover',
10166 'left' : ['r-l', [-2,0], 'right'],
10167 'right' : ['l-r', [2,0], 'left'],
10168 'bottom' : ['tl-bl', [0,2], 'top'],
10169 'top' : [ 'bl-tl', [0,-2], 'bottom']
10173 this.toolTip.render(Roo.get(document.body));
10175 this.toolTip.el.enableDisplayMode("block");
10177 Roo.get(document.body).on('click', function(){
10181 Roo.get(document.body).on('touchstart', function(){
10185 this.isApplied = true
10188 mask : function(form, target)
10192 this.target = target;
10194 if(!this.form.errorMask || !target.el){
10198 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10200 Roo.log(scrollable);
10202 var ot = this.target.el.calcOffsetsTo(scrollable);
10204 var scrollTo = ot[1] - this.form.maskOffset;
10206 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10208 scrollable.scrollTo('top', scrollTo);
10210 var box = this.target.el.getBox();
10212 var zIndex = Roo.bootstrap.Modal.zIndex++;
10215 this.maskEl.top.setStyle('position', 'absolute');
10216 this.maskEl.top.setStyle('z-index', zIndex);
10217 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218 this.maskEl.top.setLeft(0);
10219 this.maskEl.top.setTop(0);
10220 this.maskEl.top.show();
10222 this.maskEl.left.setStyle('position', 'absolute');
10223 this.maskEl.left.setStyle('z-index', zIndex);
10224 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225 this.maskEl.left.setLeft(0);
10226 this.maskEl.left.setTop(box.y - this.padding);
10227 this.maskEl.left.show();
10229 this.maskEl.bottom.setStyle('position', 'absolute');
10230 this.maskEl.bottom.setStyle('z-index', zIndex);
10231 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232 this.maskEl.bottom.setLeft(0);
10233 this.maskEl.bottom.setTop(box.bottom + this.padding);
10234 this.maskEl.bottom.show();
10236 this.maskEl.right.setStyle('position', 'absolute');
10237 this.maskEl.right.setStyle('z-index', zIndex);
10238 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239 this.maskEl.right.setLeft(box.right + this.padding);
10240 this.maskEl.right.setTop(box.y - this.padding);
10241 this.maskEl.right.show();
10243 this.toolTip.bindEl = this.target.el;
10245 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10247 var tip = this.target.blankText;
10249 if(this.target.getValue() !== '' ) {
10251 if (this.target.invalidText.length) {
10252 tip = this.target.invalidText;
10253 } else if (this.target.regexText.length){
10254 tip = this.target.regexText;
10258 this.toolTip.show(tip);
10260 this.intervalID = window.setInterval(function() {
10261 Roo.bootstrap.Form.popover.unmask();
10264 window.onwheel = function(){ return false;};
10266 (function(){ this.isMasked = true; }).defer(500, this);
10270 unmask : function()
10272 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10276 this.maskEl.top.setStyle('position', 'absolute');
10277 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278 this.maskEl.top.hide();
10280 this.maskEl.left.setStyle('position', 'absolute');
10281 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282 this.maskEl.left.hide();
10284 this.maskEl.bottom.setStyle('position', 'absolute');
10285 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286 this.maskEl.bottom.hide();
10288 this.maskEl.right.setStyle('position', 'absolute');
10289 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290 this.maskEl.right.hide();
10292 this.toolTip.hide();
10294 this.toolTip.el.hide();
10296 window.onwheel = function(){ return true;};
10298 if(this.intervalID){
10299 window.clearInterval(this.intervalID);
10300 this.intervalID = false;
10303 this.isMasked = false;
10313 * Ext JS Library 1.1.1
10314 * Copyright(c) 2006-2007, Ext JS, LLC.
10316 * Originally Released Under LGPL - original licence link has changed is not relivant.
10319 * <script type="text/javascript">
10322 * @class Roo.form.VTypes
10323 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10326 Roo.form.VTypes = function(){
10327 // closure these in so they are only created once.
10328 var alpha = /^[a-zA-Z_]+$/;
10329 var alphanum = /^[a-zA-Z0-9_]+$/;
10330 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10333 // All these messages and functions are configurable
10336 * The function used to validate email addresses
10337 * @param {String} value The email address
10339 'email' : function(v){
10340 return email.test(v);
10343 * The error text to display when the email validation function returns false
10346 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10348 * The keystroke filter mask to be applied on email input
10351 'emailMask' : /[a-z0-9_\.\-@]/i,
10354 * The function used to validate URLs
10355 * @param {String} value The URL
10357 'url' : function(v){
10358 return url.test(v);
10361 * The error text to display when the url validation function returns false
10364 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10367 * The function used to validate alpha values
10368 * @param {String} value The value
10370 'alpha' : function(v){
10371 return alpha.test(v);
10374 * The error text to display when the alpha validation function returns false
10377 'alphaText' : 'This field should only contain letters and _',
10379 * The keystroke filter mask to be applied on alpha input
10382 'alphaMask' : /[a-z_]/i,
10385 * The function used to validate alphanumeric values
10386 * @param {String} value The value
10388 'alphanum' : function(v){
10389 return alphanum.test(v);
10392 * The error text to display when the alphanumeric validation function returns false
10395 'alphanumText' : 'This field should only contain letters, numbers and _',
10397 * The keystroke filter mask to be applied on alphanumeric input
10400 'alphanumMask' : /[a-z0-9_]/i
10410 * @class Roo.bootstrap.Input
10411 * @extends Roo.bootstrap.Component
10412 * Bootstrap Input class
10413 * @cfg {Boolean} disabled is it disabled
10414 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10415 * @cfg {String} name name of the input
10416 * @cfg {string} fieldLabel - the label associated
10417 * @cfg {string} placeholder - placeholder to put in text.
10418 * @cfg {string} before - input group add on before
10419 * @cfg {string} after - input group add on after
10420 * @cfg {string} size - (lg|sm) or leave empty..
10421 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423 * @cfg {Number} md colspan out of 12 for computer-sized screens
10424 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425 * @cfg {string} value default value of the input
10426 * @cfg {Number} labelWidth set the width of label
10427 * @cfg {Number} labellg set the width of label (1-12)
10428 * @cfg {Number} labelmd set the width of label (1-12)
10429 * @cfg {Number} labelsm set the width of label (1-12)
10430 * @cfg {Number} labelxs set the width of label (1-12)
10431 * @cfg {String} labelAlign (top|left)
10432 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434 * @cfg {String} indicatorpos (left|right) default left
10435 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10439 * @cfg {String} align (left|center|right) Default left
10440 * @cfg {Boolean} forceFeedback (true|false) Default false
10443 * Create a new Input
10444 * @param {Object} config The config object
10447 Roo.bootstrap.Input = function(config){
10449 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10454 * Fires when this field receives input focus.
10455 * @param {Roo.form.Field} this
10460 * Fires when this field loses input focus.
10461 * @param {Roo.form.Field} this
10465 * @event specialkey
10466 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10467 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468 * @param {Roo.form.Field} this
10469 * @param {Roo.EventObject} e The event object
10474 * Fires just before the field blurs if the field value has changed.
10475 * @param {Roo.form.Field} this
10476 * @param {Mixed} newValue The new value
10477 * @param {Mixed} oldValue The original value
10482 * Fires after the field has been marked as invalid.
10483 * @param {Roo.form.Field} this
10484 * @param {String} msg The validation message
10489 * Fires after the field has been validated with no errors.
10490 * @param {Roo.form.Field} this
10495 * Fires after the key up
10496 * @param {Roo.form.Field} this
10497 * @param {Roo.EventObject} e The event Object
10503 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10505 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10506 automatic validation (defaults to "keyup").
10508 validationEvent : "keyup",
10510 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10512 validateOnBlur : true,
10514 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10516 validationDelay : 250,
10518 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10520 focusClass : "x-form-focus", // not needed???
10524 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10526 invalidClass : "has-warning",
10529 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10531 validClass : "has-success",
10534 * @cfg {Boolean} hasFeedback (true|false) default true
10536 hasFeedback : true,
10539 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10541 invalidFeedbackClass : "glyphicon-warning-sign",
10544 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10546 validFeedbackClass : "glyphicon-ok",
10549 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10551 selectOnFocus : false,
10554 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10558 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10563 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10565 disableKeyFilter : false,
10568 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10572 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10576 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10578 blankText : "Please complete this mandatory field",
10581 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10585 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10587 maxLength : Number.MAX_VALUE,
10589 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10591 minLengthText : "The minimum length for this field is {0}",
10593 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10595 maxLengthText : "The maximum length for this field is {0}",
10599 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10600 * If available, this function will be called only after the basic validators all return true, and will be passed the
10601 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10605 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10606 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10607 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10611 * @cfg {String} regexText -- Depricated - use Invalid Text
10616 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10622 autocomplete: false,
10626 inputType : 'text',
10629 placeholder: false,
10634 preventMark: false,
10635 isFormField : true,
10638 labelAlign : false,
10641 formatedValue : false,
10642 forceFeedback : false,
10644 indicatorpos : 'left',
10654 parentLabelAlign : function()
10657 while (parent.parent()) {
10658 parent = parent.parent();
10659 if (typeof(parent.labelAlign) !='undefined') {
10660 return parent.labelAlign;
10667 getAutoCreate : function()
10669 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10675 if(this.inputType != 'hidden'){
10676 cfg.cls = 'form-group' //input-group
10682 type : this.inputType,
10683 value : this.value,
10684 cls : 'form-control',
10685 placeholder : this.placeholder || '',
10686 autocomplete : this.autocomplete || 'new-password'
10688 if (this.inputType == 'file') {
10689 input.style = 'overflow:hidden'; // why not in CSS?
10692 if(this.capture.length){
10693 input.capture = this.capture;
10696 if(this.accept.length){
10697 input.accept = this.accept + "/*";
10701 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10704 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10705 input.maxLength = this.maxLength;
10708 if (this.disabled) {
10709 input.disabled=true;
10712 if (this.readOnly) {
10713 input.readonly=true;
10717 input.name = this.name;
10721 input.cls += ' input-' + this.size;
10725 ['xs','sm','md','lg'].map(function(size){
10726 if (settings[size]) {
10727 cfg.cls += ' col-' + size + '-' + settings[size];
10731 var inputblock = input;
10735 cls: 'glyphicon form-control-feedback'
10738 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10741 cls : 'has-feedback',
10749 if (this.before || this.after) {
10752 cls : 'input-group',
10756 if (this.before && typeof(this.before) == 'string') {
10758 inputblock.cn.push({
10760 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10764 if (this.before && typeof(this.before) == 'object') {
10765 this.before = Roo.factory(this.before);
10767 inputblock.cn.push({
10769 cls : 'roo-input-before input-group-prepend input-group-' +
10770 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10774 inputblock.cn.push(input);
10776 if (this.after && typeof(this.after) == 'string') {
10777 inputblock.cn.push({
10779 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10783 if (this.after && typeof(this.after) == 'object') {
10784 this.after = Roo.factory(this.after);
10786 inputblock.cn.push({
10788 cls : 'roo-input-after input-group-append input-group-' +
10789 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10793 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10794 inputblock.cls += ' has-feedback';
10795 inputblock.cn.push(feedback);
10800 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10801 tooltip : 'This field is required'
10803 if (this.allowBlank ) {
10804 indicator.style = this.allowBlank ? ' display:none' : '';
10806 if (align ==='left' && this.fieldLabel.length) {
10808 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10815 cls : 'control-label col-form-label',
10816 html : this.fieldLabel
10827 var labelCfg = cfg.cn[1];
10828 var contentCfg = cfg.cn[2];
10830 if(this.indicatorpos == 'right'){
10835 cls : 'control-label col-form-label',
10839 html : this.fieldLabel
10853 labelCfg = cfg.cn[0];
10854 contentCfg = cfg.cn[1];
10858 if(this.labelWidth > 12){
10859 labelCfg.style = "width: " + this.labelWidth + 'px';
10862 if(this.labelWidth < 13 && this.labelmd == 0){
10863 this.labelmd = this.labelWidth;
10866 if(this.labellg > 0){
10867 labelCfg.cls += ' col-lg-' + this.labellg;
10868 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10871 if(this.labelmd > 0){
10872 labelCfg.cls += ' col-md-' + this.labelmd;
10873 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10876 if(this.labelsm > 0){
10877 labelCfg.cls += ' col-sm-' + this.labelsm;
10878 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10881 if(this.labelxs > 0){
10882 labelCfg.cls += ' col-xs-' + this.labelxs;
10883 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10887 } else if ( this.fieldLabel.length) {
10894 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10895 tooltip : 'This field is required',
10896 style : this.allowBlank ? ' display:none' : ''
10900 //cls : 'input-group-addon',
10901 html : this.fieldLabel
10909 if(this.indicatorpos == 'right'){
10914 //cls : 'input-group-addon',
10915 html : this.fieldLabel
10920 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10921 tooltip : 'This field is required',
10922 style : this.allowBlank ? ' display:none' : ''
10942 if (this.parentType === 'Navbar' && this.parent().bar) {
10943 cfg.cls += ' navbar-form';
10946 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10947 // on BS4 we do this only if not form
10948 cfg.cls += ' navbar-form';
10956 * return the real input element.
10958 inputEl: function ()
10960 return this.el.select('input.form-control',true).first();
10963 tooltipEl : function()
10965 return this.inputEl();
10968 indicatorEl : function()
10970 if (Roo.bootstrap.version == 4) {
10971 return false; // not enabled in v4 yet.
10974 var indicator = this.el.select('i.roo-required-indicator',true).first();
10984 setDisabled : function(v)
10986 var i = this.inputEl().dom;
10988 i.removeAttribute('disabled');
10992 i.setAttribute('disabled','true');
10994 initEvents : function()
10997 this.inputEl().on("keydown" , this.fireKey, this);
10998 this.inputEl().on("focus", this.onFocus, this);
10999 this.inputEl().on("blur", this.onBlur, this);
11001 this.inputEl().relayEvent('keyup', this);
11003 this.indicator = this.indicatorEl();
11005 if(this.indicator){
11006 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11009 // reference to original value for reset
11010 this.originalValue = this.getValue();
11011 //Roo.form.TextField.superclass.initEvents.call(this);
11012 if(this.validationEvent == 'keyup'){
11013 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11014 this.inputEl().on('keyup', this.filterValidation, this);
11016 else if(this.validationEvent !== false){
11017 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11020 if(this.selectOnFocus){
11021 this.on("focus", this.preFocus, this);
11024 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11025 this.inputEl().on("keypress", this.filterKeys, this);
11027 this.inputEl().relayEvent('keypress', this);
11030 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11031 this.el.on("click", this.autoSize, this);
11034 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11035 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11038 if (typeof(this.before) == 'object') {
11039 this.before.render(this.el.select('.roo-input-before',true).first());
11041 if (typeof(this.after) == 'object') {
11042 this.after.render(this.el.select('.roo-input-after',true).first());
11045 this.inputEl().on('change', this.onChange, this);
11048 filterValidation : function(e){
11049 if(!e.isNavKeyPress()){
11050 this.validationTask.delay(this.validationDelay);
11054 * Validates the field value
11055 * @return {Boolean} True if the value is valid, else false
11057 validate : function(){
11058 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11059 if(this.disabled || this.validateValue(this.getRawValue())){
11064 this.markInvalid();
11070 * Validates a value according to the field's validation rules and marks the field as invalid
11071 * if the validation fails
11072 * @param {Mixed} value The value to validate
11073 * @return {Boolean} True if the value is valid, else false
11075 validateValue : function(value)
11077 if(this.getVisibilityEl().hasClass('hidden')){
11081 if(value.length < 1) { // if it's blank
11082 if(this.allowBlank){
11088 if(value.length < this.minLength){
11091 if(value.length > this.maxLength){
11095 var vt = Roo.form.VTypes;
11096 if(!vt[this.vtype](value, this)){
11100 if(typeof this.validator == "function"){
11101 var msg = this.validator(value);
11105 if (typeof(msg) == 'string') {
11106 this.invalidText = msg;
11110 if(this.regex && !this.regex.test(value)){
11118 fireKey : function(e){
11119 //Roo.log('field ' + e.getKey());
11120 if(e.isNavKeyPress()){
11121 this.fireEvent("specialkey", this, e);
11124 focus : function (selectText){
11126 this.inputEl().focus();
11127 if(selectText === true){
11128 this.inputEl().dom.select();
11134 onFocus : function(){
11135 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136 // this.el.addClass(this.focusClass);
11138 if(!this.hasFocus){
11139 this.hasFocus = true;
11140 this.startValue = this.getValue();
11141 this.fireEvent("focus", this);
11145 beforeBlur : Roo.emptyFn,
11149 onBlur : function(){
11151 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11152 //this.el.removeClass(this.focusClass);
11154 this.hasFocus = false;
11155 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11158 var v = this.getValue();
11159 if(String(v) !== String(this.startValue)){
11160 this.fireEvent('change', this, v, this.startValue);
11162 this.fireEvent("blur", this);
11165 onChange : function(e)
11167 var v = this.getValue();
11168 if(String(v) !== String(this.startValue)){
11169 this.fireEvent('change', this, v, this.startValue);
11175 * Resets the current field value to the originally loaded value and clears any validation messages
11177 reset : function(){
11178 this.setValue(this.originalValue);
11182 * Returns the name of the field
11183 * @return {Mixed} name The name field
11185 getName: function(){
11189 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11190 * @return {Mixed} value The field value
11192 getValue : function(){
11194 var v = this.inputEl().getValue();
11199 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11200 * @return {Mixed} value The field value
11202 getRawValue : function(){
11203 var v = this.inputEl().getValue();
11209 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11210 * @param {Mixed} value The value to set
11212 setRawValue : function(v){
11213 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11216 selectText : function(start, end){
11217 var v = this.getRawValue();
11219 start = start === undefined ? 0 : start;
11220 end = end === undefined ? v.length : end;
11221 var d = this.inputEl().dom;
11222 if(d.setSelectionRange){
11223 d.setSelectionRange(start, end);
11224 }else if(d.createTextRange){
11225 var range = d.createTextRange();
11226 range.moveStart("character", start);
11227 range.moveEnd("character", v.length-end);
11234 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11235 * @param {Mixed} value The value to set
11237 setValue : function(v){
11240 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11246 processValue : function(value){
11247 if(this.stripCharsRe){
11248 var newValue = value.replace(this.stripCharsRe, '');
11249 if(newValue !== value){
11250 this.setRawValue(newValue);
11257 preFocus : function(){
11259 if(this.selectOnFocus){
11260 this.inputEl().dom.select();
11263 filterKeys : function(e){
11264 var k = e.getKey();
11265 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11268 var c = e.getCharCode(), cc = String.fromCharCode(c);
11269 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11272 if(!this.maskRe.test(cc)){
11277 * Clear any invalid styles/messages for this field
11279 clearInvalid : function(){
11281 if(!this.el || this.preventMark){ // not rendered
11286 this.el.removeClass([this.invalidClass, 'is-invalid']);
11288 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11290 var feedback = this.el.select('.form-control-feedback', true).first();
11293 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11298 if(this.indicator){
11299 this.indicator.removeClass('visible');
11300 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11303 this.fireEvent('valid', this);
11307 * Mark this field as valid
11309 markValid : function()
11311 if(!this.el || this.preventMark){ // not rendered...
11315 this.el.removeClass([this.invalidClass, this.validClass]);
11316 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11318 var feedback = this.el.select('.form-control-feedback', true).first();
11321 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11324 if(this.indicator){
11325 this.indicator.removeClass('visible');
11326 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11334 if(this.allowBlank && !this.getRawValue().length){
11337 if (Roo.bootstrap.version == 3) {
11338 this.el.addClass(this.validClass);
11340 this.inputEl().addClass('is-valid');
11343 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11345 var feedback = this.el.select('.form-control-feedback', true).first();
11348 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11349 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11354 this.fireEvent('valid', this);
11358 * Mark this field as invalid
11359 * @param {String} msg The validation message
11361 markInvalid : function(msg)
11363 if(!this.el || this.preventMark){ // not rendered
11367 this.el.removeClass([this.invalidClass, this.validClass]);
11368 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11370 var feedback = this.el.select('.form-control-feedback', true).first();
11373 this.el.select('.form-control-feedback', true).first().removeClass(
11374 [this.invalidFeedbackClass, this.validFeedbackClass]);
11381 if(this.allowBlank && !this.getRawValue().length){
11385 if(this.indicator){
11386 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11387 this.indicator.addClass('visible');
11389 if (Roo.bootstrap.version == 3) {
11390 this.el.addClass(this.invalidClass);
11392 this.inputEl().addClass('is-invalid');
11397 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11399 var feedback = this.el.select('.form-control-feedback', true).first();
11402 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11404 if(this.getValue().length || this.forceFeedback){
11405 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11412 this.fireEvent('invalid', this, msg);
11415 SafariOnKeyDown : function(event)
11417 // this is a workaround for a password hang bug on chrome/ webkit.
11418 if (this.inputEl().dom.type != 'password') {
11422 var isSelectAll = false;
11424 if(this.inputEl().dom.selectionEnd > 0){
11425 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11427 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11428 event.preventDefault();
11433 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11435 event.preventDefault();
11436 // this is very hacky as keydown always get's upper case.
11438 var cc = String.fromCharCode(event.getCharCode());
11439 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11443 adjustWidth : function(tag, w){
11444 tag = tag.toLowerCase();
11445 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11446 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11447 if(tag == 'input'){
11450 if(tag == 'textarea'){
11453 }else if(Roo.isOpera){
11454 if(tag == 'input'){
11457 if(tag == 'textarea'){
11465 setFieldLabel : function(v)
11467 if(!this.rendered){
11471 if(this.indicatorEl()){
11472 var ar = this.el.select('label > span',true);
11474 if (ar.elements.length) {
11475 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476 this.fieldLabel = v;
11480 var br = this.el.select('label',true);
11482 if(br.elements.length) {
11483 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484 this.fieldLabel = v;
11488 Roo.log('Cannot Found any of label > span || label in input');
11492 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11493 this.fieldLabel = v;
11508 * @class Roo.bootstrap.TextArea
11509 * @extends Roo.bootstrap.Input
11510 * Bootstrap TextArea class
11511 * @cfg {Number} cols Specifies the visible width of a text area
11512 * @cfg {Number} rows Specifies the visible number of lines in a text area
11513 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11514 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11515 * @cfg {string} html text
11518 * Create a new TextArea
11519 * @param {Object} config The config object
11522 Roo.bootstrap.TextArea = function(config){
11523 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11527 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11537 getAutoCreate : function(){
11539 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11545 if(this.inputType != 'hidden'){
11546 cfg.cls = 'form-group' //input-group
11554 value : this.value || '',
11555 html: this.html || '',
11556 cls : 'form-control',
11557 placeholder : this.placeholder || ''
11561 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11562 input.maxLength = this.maxLength;
11566 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11570 input.cols = this.cols;
11573 if (this.readOnly) {
11574 input.readonly = true;
11578 input.name = this.name;
11582 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11586 ['xs','sm','md','lg'].map(function(size){
11587 if (settings[size]) {
11588 cfg.cls += ' col-' + size + '-' + settings[size];
11592 var inputblock = input;
11594 if(this.hasFeedback && !this.allowBlank){
11598 cls: 'glyphicon form-control-feedback'
11602 cls : 'has-feedback',
11611 if (this.before || this.after) {
11614 cls : 'input-group',
11618 inputblock.cn.push({
11620 cls : 'input-group-addon',
11625 inputblock.cn.push(input);
11627 if(this.hasFeedback && !this.allowBlank){
11628 inputblock.cls += ' has-feedback';
11629 inputblock.cn.push(feedback);
11633 inputblock.cn.push({
11635 cls : 'input-group-addon',
11642 if (align ==='left' && this.fieldLabel.length) {
11647 cls : 'control-label',
11648 html : this.fieldLabel
11659 if(this.labelWidth > 12){
11660 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11663 if(this.labelWidth < 13 && this.labelmd == 0){
11664 this.labelmd = this.labelWidth;
11667 if(this.labellg > 0){
11668 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11669 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11672 if(this.labelmd > 0){
11673 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11674 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11677 if(this.labelsm > 0){
11678 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11679 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11682 if(this.labelxs > 0){
11683 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11684 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11687 } else if ( this.fieldLabel.length) {
11692 //cls : 'input-group-addon',
11693 html : this.fieldLabel
11711 if (this.disabled) {
11712 input.disabled=true;
11719 * return the real textarea element.
11721 inputEl: function ()
11723 return this.el.select('textarea.form-control',true).first();
11727 * Clear any invalid styles/messages for this field
11729 clearInvalid : function()
11732 if(!this.el || this.preventMark){ // not rendered
11736 var label = this.el.select('label', true).first();
11737 var icon = this.el.select('i.fa-star', true).first();
11742 this.el.removeClass( this.validClass);
11743 this.inputEl().removeClass('is-invalid');
11745 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11747 var feedback = this.el.select('.form-control-feedback', true).first();
11750 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11755 this.fireEvent('valid', this);
11759 * Mark this field as valid
11761 markValid : function()
11763 if(!this.el || this.preventMark){ // not rendered
11767 this.el.removeClass([this.invalidClass, this.validClass]);
11768 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11770 var feedback = this.el.select('.form-control-feedback', true).first();
11773 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11776 if(this.disabled || this.allowBlank){
11780 var label = this.el.select('label', true).first();
11781 var icon = this.el.select('i.fa-star', true).first();
11786 if (Roo.bootstrap.version == 3) {
11787 this.el.addClass(this.validClass);
11789 this.inputEl().addClass('is-valid');
11793 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11795 var feedback = this.el.select('.form-control-feedback', true).first();
11798 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11799 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11804 this.fireEvent('valid', this);
11808 * Mark this field as invalid
11809 * @param {String} msg The validation message
11811 markInvalid : function(msg)
11813 if(!this.el || this.preventMark){ // not rendered
11817 this.el.removeClass([this.invalidClass, this.validClass]);
11818 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11820 var feedback = this.el.select('.form-control-feedback', true).first();
11823 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11826 if(this.disabled || this.allowBlank){
11830 var label = this.el.select('label', true).first();
11831 var icon = this.el.select('i.fa-star', true).first();
11833 if(!this.getValue().length && label && !icon){
11834 this.el.createChild({
11836 cls : 'text-danger fa fa-lg fa-star',
11837 tooltip : 'This field is required',
11838 style : 'margin-right:5px;'
11842 if (Roo.bootstrap.version == 3) {
11843 this.el.addClass(this.invalidClass);
11845 this.inputEl().addClass('is-invalid');
11848 // fixme ... this may be depricated need to test..
11849 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11851 var feedback = this.el.select('.form-control-feedback', true).first();
11854 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11856 if(this.getValue().length || this.forceFeedback){
11857 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11864 this.fireEvent('invalid', this, msg);
11872 * trigger field - base class for combo..
11877 * @class Roo.bootstrap.TriggerField
11878 * @extends Roo.bootstrap.Input
11879 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11880 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11881 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11882 * for which you can provide a custom implementation. For example:
11884 var trigger = new Roo.bootstrap.TriggerField();
11885 trigger.onTriggerClick = myTriggerFn;
11886 trigger.applyTo('my-field');
11889 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11890 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11891 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11892 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11893 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11896 * Create a new TriggerField.
11897 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11898 * to the base TextField)
11900 Roo.bootstrap.TriggerField = function(config){
11901 this.mimicing = false;
11902 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11905 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11907 * @cfg {String} triggerClass A CSS class to apply to the trigger
11910 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11915 * @cfg {Boolean} removable (true|false) special filter default false
11919 /** @cfg {Boolean} grow @hide */
11920 /** @cfg {Number} growMin @hide */
11921 /** @cfg {Number} growMax @hide */
11927 autoSize: Roo.emptyFn,
11931 deferHeight : true,
11934 actionMode : 'wrap',
11939 getAutoCreate : function(){
11941 var align = this.labelAlign || this.parentLabelAlign();
11946 cls: 'form-group' //input-group
11953 type : this.inputType,
11954 cls : 'form-control',
11955 autocomplete: 'new-password',
11956 placeholder : this.placeholder || ''
11960 input.name = this.name;
11963 input.cls += ' input-' + this.size;
11966 if (this.disabled) {
11967 input.disabled=true;
11970 var inputblock = input;
11972 if(this.hasFeedback && !this.allowBlank){
11976 cls: 'glyphicon form-control-feedback'
11979 if(this.removable && !this.editable ){
11981 cls : 'has-feedback',
11987 cls : 'roo-combo-removable-btn close'
11994 cls : 'has-feedback',
12003 if(this.removable && !this.editable ){
12005 cls : 'roo-removable',
12011 cls : 'roo-combo-removable-btn close'
12018 if (this.before || this.after) {
12021 cls : 'input-group',
12025 inputblock.cn.push({
12027 cls : 'input-group-addon input-group-prepend input-group-text',
12032 inputblock.cn.push(input);
12034 if(this.hasFeedback && !this.allowBlank){
12035 inputblock.cls += ' has-feedback';
12036 inputblock.cn.push(feedback);
12040 inputblock.cn.push({
12042 cls : 'input-group-addon input-group-append input-group-text',
12051 var ibwrap = inputblock;
12056 cls: 'roo-select2-choices',
12060 cls: 'roo-select2-search-field',
12072 cls: 'roo-select2-container input-group',
12077 cls: 'form-hidden-field'
12083 if(!this.multiple && this.showToggleBtn){
12089 if (this.caret != false) {
12092 cls: 'fa fa-' + this.caret
12099 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12101 Roo.bootstrap.version == 3 ? caret : '',
12104 cls: 'combobox-clear',
12118 combobox.cls += ' roo-select2-container-multi';
12122 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12123 tooltip : 'This field is required'
12125 if (Roo.bootstrap.version == 4) {
12128 style : 'display:none'
12133 if (align ==='left' && this.fieldLabel.length) {
12135 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12142 cls : 'control-label',
12143 html : this.fieldLabel
12155 var labelCfg = cfg.cn[1];
12156 var contentCfg = cfg.cn[2];
12158 if(this.indicatorpos == 'right'){
12163 cls : 'control-label',
12167 html : this.fieldLabel
12181 labelCfg = cfg.cn[0];
12182 contentCfg = cfg.cn[1];
12185 if(this.labelWidth > 12){
12186 labelCfg.style = "width: " + this.labelWidth + 'px';
12189 if(this.labelWidth < 13 && this.labelmd == 0){
12190 this.labelmd = this.labelWidth;
12193 if(this.labellg > 0){
12194 labelCfg.cls += ' col-lg-' + this.labellg;
12195 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12198 if(this.labelmd > 0){
12199 labelCfg.cls += ' col-md-' + this.labelmd;
12200 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12203 if(this.labelsm > 0){
12204 labelCfg.cls += ' col-sm-' + this.labelsm;
12205 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12208 if(this.labelxs > 0){
12209 labelCfg.cls += ' col-xs-' + this.labelxs;
12210 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12213 } else if ( this.fieldLabel.length) {
12214 // Roo.log(" label");
12219 //cls : 'input-group-addon',
12220 html : this.fieldLabel
12228 if(this.indicatorpos == 'right'){
12236 html : this.fieldLabel
12250 // Roo.log(" no label && no align");
12257 ['xs','sm','md','lg'].map(function(size){
12258 if (settings[size]) {
12259 cfg.cls += ' col-' + size + '-' + settings[size];
12270 onResize : function(w, h){
12271 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12272 // if(typeof w == 'number'){
12273 // var x = w - this.trigger.getWidth();
12274 // this.inputEl().setWidth(this.adjustWidth('input', x));
12275 // this.trigger.setStyle('left', x+'px');
12280 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12283 getResizeEl : function(){
12284 return this.inputEl();
12288 getPositionEl : function(){
12289 return this.inputEl();
12293 alignErrorIcon : function(){
12294 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12298 initEvents : function(){
12302 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12303 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12304 if(!this.multiple && this.showToggleBtn){
12305 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12306 if(this.hideTrigger){
12307 this.trigger.setDisplayed(false);
12309 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12313 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12316 if(this.removable && !this.editable && !this.tickable){
12317 var close = this.closeTriggerEl();
12320 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12321 close.on('click', this.removeBtnClick, this, close);
12325 //this.trigger.addClassOnOver('x-form-trigger-over');
12326 //this.trigger.addClassOnClick('x-form-trigger-click');
12329 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12333 closeTriggerEl : function()
12335 var close = this.el.select('.roo-combo-removable-btn', true).first();
12336 return close ? close : false;
12339 removeBtnClick : function(e, h, el)
12341 e.preventDefault();
12343 if(this.fireEvent("remove", this) !== false){
12345 this.fireEvent("afterremove", this)
12349 createList : function()
12351 this.list = Roo.get(document.body).createChild({
12352 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12353 cls: 'typeahead typeahead-long dropdown-menu shadow',
12354 style: 'display:none'
12357 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12362 initTrigger : function(){
12367 onDestroy : function(){
12369 this.trigger.removeAllListeners();
12370 // this.trigger.remove();
12373 // this.wrap.remove();
12375 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12379 onFocus : function(){
12380 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12382 if(!this.mimicing){
12383 this.wrap.addClass('x-trigger-wrap-focus');
12384 this.mimicing = true;
12385 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12386 if(this.monitorTab){
12387 this.el.on("keydown", this.checkTab, this);
12394 checkTab : function(e){
12395 if(e.getKey() == e.TAB){
12396 this.triggerBlur();
12401 onBlur : function(){
12406 mimicBlur : function(e, t){
12408 if(!this.wrap.contains(t) && this.validateBlur()){
12409 this.triggerBlur();
12415 triggerBlur : function(){
12416 this.mimicing = false;
12417 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12418 if(this.monitorTab){
12419 this.el.un("keydown", this.checkTab, this);
12421 //this.wrap.removeClass('x-trigger-wrap-focus');
12422 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12426 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12427 validateBlur : function(e, t){
12432 onDisable : function(){
12433 this.inputEl().dom.disabled = true;
12434 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12436 // this.wrap.addClass('x-item-disabled');
12441 onEnable : function(){
12442 this.inputEl().dom.disabled = false;
12443 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12445 // this.el.removeClass('x-item-disabled');
12450 onShow : function(){
12451 var ae = this.getActionEl();
12454 ae.dom.style.display = '';
12455 ae.dom.style.visibility = 'visible';
12461 onHide : function(){
12462 var ae = this.getActionEl();
12463 ae.dom.style.display = 'none';
12467 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12468 * by an implementing function.
12470 * @param {EventObject} e
12472 onTriggerClick : Roo.emptyFn
12480 * @class Roo.bootstrap.CardUploader
12481 * @extends Roo.bootstrap.Button
12482 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12483 * @cfg {Number} errorTimeout default 3000
12484 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12485 * @cfg {Array} html The button text.
12489 * Create a new CardUploader
12490 * @param {Object} config The config object
12493 Roo.bootstrap.CardUploader = function(config){
12497 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12500 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12508 * When a image is clicked on - and needs to display a slideshow or similar..
12509 * @param {Roo.bootstrap.Card} this
12510 * @param {Object} The image information data
12516 * When a the download link is clicked
12517 * @param {Roo.bootstrap.Card} this
12518 * @param {Object} The image information data contains
12525 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12528 errorTimeout : 3000,
12532 fileCollection : false,
12535 getAutoCreate : function()
12539 cls :'form-group' ,
12544 //cls : 'input-group-addon',
12545 html : this.fieldLabel
12553 value : this.value,
12554 cls : 'd-none form-control'
12559 multiple : 'multiple',
12561 cls : 'd-none roo-card-upload-selector'
12565 cls : 'roo-card-uploader-button-container w-100 mb-2'
12568 cls : 'card-columns roo-card-uploader-container'
12578 getChildContainer : function() /// what children are added to.
12580 return this.containerEl;
12583 getButtonContainer : function() /// what children are added to.
12585 return this.el.select(".roo-card-uploader-button-container").first();
12588 initEvents : function()
12591 Roo.bootstrap.Input.prototype.initEvents.call(this);
12595 xns: Roo.bootstrap,
12598 container_method : 'getButtonContainer' ,
12599 html : this.html, // fix changable?
12602 'click' : function(btn, e) {
12611 this.urlAPI = (window.createObjectURL && window) ||
12612 (window.URL && URL.revokeObjectURL && URL) ||
12613 (window.webkitURL && webkitURL);
12618 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12620 this.selectorEl.on('change', this.onFileSelected, this);
12623 this.images.forEach(function(img) {
12626 this.images = false;
12628 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12634 onClick : function(e)
12636 e.preventDefault();
12638 this.selectorEl.dom.click();
12642 onFileSelected : function(e)
12644 e.preventDefault();
12646 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12650 Roo.each(this.selectorEl.dom.files, function(file){
12651 this.addFile(file);
12660 addFile : function(file)
12663 if(typeof(file) === 'string'){
12664 throw "Add file by name?"; // should not happen
12668 if(!file || !this.urlAPI){
12678 var url = _this.urlAPI.createObjectURL( file);
12681 id : Roo.bootstrap.CardUploader.ID--,
12682 is_uploaded : false,
12686 mimetype : file.type,
12694 * addCard - add an Attachment to the uploader
12695 * @param data - the data about the image to upload
12699 title : "Title of file",
12700 is_uploaded : false,
12701 src : "http://.....",
12702 srcfile : { the File upload object },
12703 mimetype : file.type,
12706 .. any other data...
12712 addCard : function (data)
12714 // hidden input element?
12715 // if the file is not an image...
12716 //then we need to use something other that and header_image
12721 xns : Roo.bootstrap,
12722 xtype : 'CardFooter',
12725 xns : Roo.bootstrap,
12731 xns : Roo.bootstrap,
12733 html : String.format("<small>{0}</small>", data.title),
12734 cls : 'col-10 text-left',
12739 click : function() {
12741 t.fireEvent( "download", t, data );
12747 xns : Roo.bootstrap,
12749 style: 'max-height: 28px; ',
12755 click : function() {
12756 t.removeCard(data.id)
12768 var cn = this.addxtype(
12771 xns : Roo.bootstrap,
12774 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12775 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12776 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12781 initEvents : function() {
12782 Roo.bootstrap.Card.prototype.initEvents.call(this);
12784 this.imgEl = this.el.select('.card-img-top').first();
12786 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12787 this.imgEl.set({ 'pointer' : 'cursor' });
12790 this.getCardFooter().addClass('p-1');
12797 // dont' really need ot update items.
12798 // this.items.push(cn);
12799 this.fileCollection.add(cn);
12801 if (!data.srcfile) {
12802 this.updateInput();
12807 var reader = new FileReader();
12808 reader.addEventListener("load", function() {
12809 data.srcdata = reader.result;
12812 reader.readAsDataURL(data.srcfile);
12817 removeCard : function(id)
12820 var card = this.fileCollection.get(id);
12821 card.data.is_deleted = 1;
12822 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12823 //this.fileCollection.remove(card);
12824 //this.items = this.items.filter(function(e) { return e != card });
12825 // dont' really need ot update items.
12826 card.el.dom.parentNode.removeChild(card.el.dom);
12827 this.updateInput();
12833 this.fileCollection.each(function(card) {
12834 if (card.el.dom && card.el.dom.parentNode) {
12835 card.el.dom.parentNode.removeChild(card.el.dom);
12838 this.fileCollection.clear();
12839 this.updateInput();
12842 updateInput : function()
12845 this.fileCollection.each(function(e) {
12849 this.inputEl().dom.value = JSON.stringify(data);
12859 Roo.bootstrap.CardUploader.ID = -1;/*
12861 * Ext JS Library 1.1.1
12862 * Copyright(c) 2006-2007, Ext JS, LLC.
12864 * Originally Released Under LGPL - original licence link has changed is not relivant.
12867 * <script type="text/javascript">
12872 * @class Roo.data.SortTypes
12874 * Defines the default sorting (casting?) comparison functions used when sorting data.
12876 Roo.data.SortTypes = {
12878 * Default sort that does nothing
12879 * @param {Mixed} s The value being converted
12880 * @return {Mixed} The comparison value
12882 none : function(s){
12887 * The regular expression used to strip tags
12891 stripTagsRE : /<\/?[^>]+>/gi,
12894 * Strips all HTML tags to sort on text only
12895 * @param {Mixed} s The value being converted
12896 * @return {String} The comparison value
12898 asText : function(s){
12899 return String(s).replace(this.stripTagsRE, "");
12903 * Strips all HTML tags to sort on text only - Case insensitive
12904 * @param {Mixed} s The value being converted
12905 * @return {String} The comparison value
12907 asUCText : function(s){
12908 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12912 * Case insensitive string
12913 * @param {Mixed} s The value being converted
12914 * @return {String} The comparison value
12916 asUCString : function(s) {
12917 return String(s).toUpperCase();
12922 * @param {Mixed} s The value being converted
12923 * @return {Number} The comparison value
12925 asDate : function(s) {
12929 if(s instanceof Date){
12930 return s.getTime();
12932 return Date.parse(String(s));
12937 * @param {Mixed} s The value being converted
12938 * @return {Float} The comparison value
12940 asFloat : function(s) {
12941 var val = parseFloat(String(s).replace(/,/g, ""));
12950 * @param {Mixed} s The value being converted
12951 * @return {Number} The comparison value
12953 asInt : function(s) {
12954 var val = parseInt(String(s).replace(/,/g, ""));
12962 * Ext JS Library 1.1.1
12963 * Copyright(c) 2006-2007, Ext JS, LLC.
12965 * Originally Released Under LGPL - original licence link has changed is not relivant.
12968 * <script type="text/javascript">
12972 * @class Roo.data.Record
12973 * Instances of this class encapsulate both record <em>definition</em> information, and record
12974 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12975 * to access Records cached in an {@link Roo.data.Store} object.<br>
12977 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12978 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12981 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12983 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12984 * {@link #create}. The parameters are the same.
12985 * @param {Array} data An associative Array of data values keyed by the field name.
12986 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12987 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12988 * not specified an integer id is generated.
12990 Roo.data.Record = function(data, id){
12991 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12996 * Generate a constructor for a specific record layout.
12997 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12998 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12999 * Each field definition object may contain the following properties: <ul>
13000 * <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,
13001 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13002 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13003 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13004 * is being used, then this is a string containing the javascript expression to reference the data relative to
13005 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13006 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13007 * this may be omitted.</p></li>
13008 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13009 * <ul><li>auto (Default, implies no conversion)</li>
13014 * <li>date</li></ul></p></li>
13015 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13016 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13017 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13018 * by the Reader into an object that will be stored in the Record. It is passed the
13019 * following parameters:<ul>
13020 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13022 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13024 * <br>usage:<br><pre><code>
13025 var TopicRecord = Roo.data.Record.create(
13026 {name: 'title', mapping: 'topic_title'},
13027 {name: 'author', mapping: 'username'},
13028 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13029 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13030 {name: 'lastPoster', mapping: 'user2'},
13031 {name: 'excerpt', mapping: 'post_text'}
13034 var myNewRecord = new TopicRecord({
13035 title: 'Do my job please',
13038 lastPost: new Date(),
13039 lastPoster: 'Animal',
13040 excerpt: 'No way dude!'
13042 myStore.add(myNewRecord);
13047 Roo.data.Record.create = function(o){
13048 var f = function(){
13049 f.superclass.constructor.apply(this, arguments);
13051 Roo.extend(f, Roo.data.Record);
13052 var p = f.prototype;
13053 p.fields = new Roo.util.MixedCollection(false, function(field){
13056 for(var i = 0, len = o.length; i < len; i++){
13057 p.fields.add(new Roo.data.Field(o[i]));
13059 f.getField = function(name){
13060 return p.fields.get(name);
13065 Roo.data.Record.AUTO_ID = 1000;
13066 Roo.data.Record.EDIT = 'edit';
13067 Roo.data.Record.REJECT = 'reject';
13068 Roo.data.Record.COMMIT = 'commit';
13070 Roo.data.Record.prototype = {
13072 * Readonly flag - true if this record has been modified.
13081 join : function(store){
13082 this.store = store;
13086 * Set the named field to the specified value.
13087 * @param {String} name The name of the field to set.
13088 * @param {Object} value The value to set the field to.
13090 set : function(name, value){
13091 if(this.data[name] == value){
13095 if(!this.modified){
13096 this.modified = {};
13098 if(typeof this.modified[name] == 'undefined'){
13099 this.modified[name] = this.data[name];
13101 this.data[name] = value;
13102 if(!this.editing && this.store){
13103 this.store.afterEdit(this);
13108 * Get the value of the named field.
13109 * @param {String} name The name of the field to get the value of.
13110 * @return {Object} The value of the field.
13112 get : function(name){
13113 return this.data[name];
13117 beginEdit : function(){
13118 this.editing = true;
13119 this.modified = {};
13123 cancelEdit : function(){
13124 this.editing = false;
13125 delete this.modified;
13129 endEdit : function(){
13130 this.editing = false;
13131 if(this.dirty && this.store){
13132 this.store.afterEdit(this);
13137 * Usually called by the {@link Roo.data.Store} which owns the Record.
13138 * Rejects all changes made to the Record since either creation, or the last commit operation.
13139 * Modified fields are reverted to their original values.
13141 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13142 * of reject operations.
13144 reject : function(){
13145 var m = this.modified;
13147 if(typeof m[n] != "function"){
13148 this.data[n] = m[n];
13151 this.dirty = false;
13152 delete this.modified;
13153 this.editing = false;
13155 this.store.afterReject(this);
13160 * Usually called by the {@link Roo.data.Store} which owns the Record.
13161 * Commits all changes made to the Record since either creation, or the last commit operation.
13163 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13164 * of commit operations.
13166 commit : function(){
13167 this.dirty = false;
13168 delete this.modified;
13169 this.editing = false;
13171 this.store.afterCommit(this);
13176 hasError : function(){
13177 return this.error != null;
13181 clearError : function(){
13186 * Creates a copy of this record.
13187 * @param {String} id (optional) A new record id if you don't want to use this record's id
13190 copy : function(newId) {
13191 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13195 * Ext JS Library 1.1.1
13196 * Copyright(c) 2006-2007, Ext JS, LLC.
13198 * Originally Released Under LGPL - original licence link has changed is not relivant.
13201 * <script type="text/javascript">
13207 * @class Roo.data.Store
13208 * @extends Roo.util.Observable
13209 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13210 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13212 * 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
13213 * has no knowledge of the format of the data returned by the Proxy.<br>
13215 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13216 * instances from the data object. These records are cached and made available through accessor functions.
13218 * Creates a new Store.
13219 * @param {Object} config A config object containing the objects needed for the Store to access data,
13220 * and read the data into Records.
13222 Roo.data.Store = function(config){
13223 this.data = new Roo.util.MixedCollection(false);
13224 this.data.getKey = function(o){
13227 this.baseParams = {};
13229 this.paramNames = {
13234 "multisort" : "_multisort"
13237 if(config && config.data){
13238 this.inlineData = config.data;
13239 delete config.data;
13242 Roo.apply(this, config);
13244 if(this.reader){ // reader passed
13245 this.reader = Roo.factory(this.reader, Roo.data);
13246 this.reader.xmodule = this.xmodule || false;
13247 if(!this.recordType){
13248 this.recordType = this.reader.recordType;
13250 if(this.reader.onMetaChange){
13251 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13255 if(this.recordType){
13256 this.fields = this.recordType.prototype.fields;
13258 this.modified = [];
13262 * @event datachanged
13263 * Fires when the data cache has changed, and a widget which is using this Store
13264 * as a Record cache should refresh its view.
13265 * @param {Store} this
13267 datachanged : true,
13269 * @event metachange
13270 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13271 * @param {Store} this
13272 * @param {Object} meta The JSON metadata
13277 * Fires when Records have been added to the Store
13278 * @param {Store} this
13279 * @param {Roo.data.Record[]} records The array of Records added
13280 * @param {Number} index The index at which the record(s) were added
13285 * Fires when a Record has been removed from the Store
13286 * @param {Store} this
13287 * @param {Roo.data.Record} record The Record that was removed
13288 * @param {Number} index The index at which the record was removed
13293 * Fires when a Record has been updated
13294 * @param {Store} this
13295 * @param {Roo.data.Record} record The Record that was updated
13296 * @param {String} operation The update operation being performed. Value may be one of:
13298 Roo.data.Record.EDIT
13299 Roo.data.Record.REJECT
13300 Roo.data.Record.COMMIT
13306 * Fires when the data cache has been cleared.
13307 * @param {Store} this
13311 * @event beforeload
13312 * Fires before a request is made for a new data object. If the beforeload handler returns false
13313 * the load action will be canceled.
13314 * @param {Store} this
13315 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13319 * @event beforeloadadd
13320 * Fires after a new set of Records has been loaded.
13321 * @param {Store} this
13322 * @param {Roo.data.Record[]} records The Records that were loaded
13323 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13325 beforeloadadd : true,
13328 * Fires after a new set of Records has been loaded, before they are added to the store.
13329 * @param {Store} this
13330 * @param {Roo.data.Record[]} records The Records that were loaded
13331 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13332 * @params {Object} return from reader
13336 * @event loadexception
13337 * Fires if an exception occurs in the Proxy during loading.
13338 * Called with the signature of the Proxy's "loadexception" event.
13339 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13342 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13343 * @param {Object} load options
13344 * @param {Object} jsonData from your request (normally this contains the Exception)
13346 loadexception : true
13350 this.proxy = Roo.factory(this.proxy, Roo.data);
13351 this.proxy.xmodule = this.xmodule || false;
13352 this.relayEvents(this.proxy, ["loadexception"]);
13354 this.sortToggle = {};
13355 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13357 Roo.data.Store.superclass.constructor.call(this);
13359 if(this.inlineData){
13360 this.loadData(this.inlineData);
13361 delete this.inlineData;
13365 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13367 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13368 * without a remote query - used by combo/forms at present.
13372 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13375 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13378 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13379 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13382 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13383 * on any HTTP request
13386 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13389 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13393 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13394 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13396 remoteSort : false,
13399 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13400 * loaded or when a record is removed. (defaults to false).
13402 pruneModifiedRecords : false,
13405 lastOptions : null,
13408 * Add Records to the Store and fires the add event.
13409 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13411 add : function(records){
13412 records = [].concat(records);
13413 for(var i = 0, len = records.length; i < len; i++){
13414 records[i].join(this);
13416 var index = this.data.length;
13417 this.data.addAll(records);
13418 this.fireEvent("add", this, records, index);
13422 * Remove a Record from the Store and fires the remove event.
13423 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13425 remove : function(record){
13426 var index = this.data.indexOf(record);
13427 this.data.removeAt(index);
13429 if(this.pruneModifiedRecords){
13430 this.modified.remove(record);
13432 this.fireEvent("remove", this, record, index);
13436 * Remove all Records from the Store and fires the clear event.
13438 removeAll : function(){
13440 if(this.pruneModifiedRecords){
13441 this.modified = [];
13443 this.fireEvent("clear", this);
13447 * Inserts Records to the Store at the given index and fires the add event.
13448 * @param {Number} index The start index at which to insert the passed Records.
13449 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13451 insert : function(index, records){
13452 records = [].concat(records);
13453 for(var i = 0, len = records.length; i < len; i++){
13454 this.data.insert(index, records[i]);
13455 records[i].join(this);
13457 this.fireEvent("add", this, records, index);
13461 * Get the index within the cache of the passed Record.
13462 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13463 * @return {Number} The index of the passed Record. Returns -1 if not found.
13465 indexOf : function(record){
13466 return this.data.indexOf(record);
13470 * Get the index within the cache of the Record with the passed id.
13471 * @param {String} id The id of the Record to find.
13472 * @return {Number} The index of the Record. Returns -1 if not found.
13474 indexOfId : function(id){
13475 return this.data.indexOfKey(id);
13479 * Get the Record with the specified id.
13480 * @param {String} id The id of the Record to find.
13481 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13483 getById : function(id){
13484 return this.data.key(id);
13488 * Get the Record at the specified index.
13489 * @param {Number} index The index of the Record to find.
13490 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13492 getAt : function(index){
13493 return this.data.itemAt(index);
13497 * Returns a range of Records between specified indices.
13498 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13499 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13500 * @return {Roo.data.Record[]} An array of Records
13502 getRange : function(start, end){
13503 return this.data.getRange(start, end);
13507 storeOptions : function(o){
13508 o = Roo.apply({}, o);
13511 this.lastOptions = o;
13515 * Loads the Record cache from the configured Proxy using the configured Reader.
13517 * If using remote paging, then the first load call must specify the <em>start</em>
13518 * and <em>limit</em> properties in the options.params property to establish the initial
13519 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13521 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13522 * and this call will return before the new data has been loaded. Perform any post-processing
13523 * in a callback function, or in a "load" event handler.</strong>
13525 * @param {Object} options An object containing properties which control loading options:<ul>
13526 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13527 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13528 * passed the following arguments:<ul>
13529 * <li>r : Roo.data.Record[]</li>
13530 * <li>options: Options object from the load call</li>
13531 * <li>success: Boolean success indicator</li></ul></li>
13532 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13533 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13536 load : function(options){
13537 options = options || {};
13538 if(this.fireEvent("beforeload", this, options) !== false){
13539 this.storeOptions(options);
13540 var p = Roo.apply(options.params || {}, this.baseParams);
13541 // if meta was not loaded from remote source.. try requesting it.
13542 if (!this.reader.metaFromRemote) {
13543 p._requestMeta = 1;
13545 if(this.sortInfo && this.remoteSort){
13546 var pn = this.paramNames;
13547 p[pn["sort"]] = this.sortInfo.field;
13548 p[pn["dir"]] = this.sortInfo.direction;
13550 if (this.multiSort) {
13551 var pn = this.paramNames;
13552 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13555 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13560 * Reloads the Record cache from the configured Proxy using the configured Reader and
13561 * the options from the last load operation performed.
13562 * @param {Object} options (optional) An object containing properties which may override the options
13563 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13564 * the most recently used options are reused).
13566 reload : function(options){
13567 this.load(Roo.applyIf(options||{}, this.lastOptions));
13571 // Called as a callback by the Reader during a load operation.
13572 loadRecords : function(o, options, success){
13573 if(!o || success === false){
13574 if(success !== false){
13575 this.fireEvent("load", this, [], options, o);
13577 if(options.callback){
13578 options.callback.call(options.scope || this, [], options, false);
13582 // if data returned failure - throw an exception.
13583 if (o.success === false) {
13584 // show a message if no listener is registered.
13585 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13586 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13588 // loadmask wil be hooked into this..
13589 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13592 var r = o.records, t = o.totalRecords || r.length;
13594 this.fireEvent("beforeloadadd", this, r, options, o);
13596 if(!options || options.add !== true){
13597 if(this.pruneModifiedRecords){
13598 this.modified = [];
13600 for(var i = 0, len = r.length; i < len; i++){
13604 this.data = this.snapshot;
13605 delete this.snapshot;
13608 this.data.addAll(r);
13609 this.totalLength = t;
13611 this.fireEvent("datachanged", this);
13613 this.totalLength = Math.max(t, this.data.length+r.length);
13617 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13619 var e = new Roo.data.Record({});
13621 e.set(this.parent.displayField, this.parent.emptyTitle);
13622 e.set(this.parent.valueField, '');
13627 this.fireEvent("load", this, r, options, o);
13628 if(options.callback){
13629 options.callback.call(options.scope || this, r, options, true);
13635 * Loads data from a passed data block. A Reader which understands the format of the data
13636 * must have been configured in the constructor.
13637 * @param {Object} data The data block from which to read the Records. The format of the data expected
13638 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13639 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13641 loadData : function(o, append){
13642 var r = this.reader.readRecords(o);
13643 this.loadRecords(r, {add: append}, true);
13647 * using 'cn' the nested child reader read the child array into it's child stores.
13648 * @param {Object} rec The record with a 'children array
13650 loadDataFromChildren : function(rec)
13652 this.loadData(this.reader.toLoadData(rec));
13657 * Gets the number of cached records.
13659 * <em>If using paging, this may not be the total size of the dataset. If the data object
13660 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13661 * the data set size</em>
13663 getCount : function(){
13664 return this.data.length || 0;
13668 * Gets the total number of records in the dataset as returned by the server.
13670 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13671 * the dataset size</em>
13673 getTotalCount : function(){
13674 return this.totalLength || 0;
13678 * Returns the sort state of the Store as an object with two properties:
13680 field {String} The name of the field by which the Records are sorted
13681 direction {String} The sort order, "ASC" or "DESC"
13684 getSortState : function(){
13685 return this.sortInfo;
13689 applySort : function(){
13690 if(this.sortInfo && !this.remoteSort){
13691 var s = this.sortInfo, f = s.field;
13692 var st = this.fields.get(f).sortType;
13693 var fn = function(r1, r2){
13694 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13695 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13697 this.data.sort(s.direction, fn);
13698 if(this.snapshot && this.snapshot != this.data){
13699 this.snapshot.sort(s.direction, fn);
13705 * Sets the default sort column and order to be used by the next load operation.
13706 * @param {String} fieldName The name of the field to sort by.
13707 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13709 setDefaultSort : function(field, dir){
13710 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13714 * Sort the Records.
13715 * If remote sorting is used, the sort is performed on the server, and the cache is
13716 * reloaded. If local sorting is used, the cache is sorted internally.
13717 * @param {String} fieldName The name of the field to sort by.
13718 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13720 sort : function(fieldName, dir){
13721 var f = this.fields.get(fieldName);
13723 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13725 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13726 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13731 this.sortToggle[f.name] = dir;
13732 this.sortInfo = {field: f.name, direction: dir};
13733 if(!this.remoteSort){
13735 this.fireEvent("datachanged", this);
13737 this.load(this.lastOptions);
13742 * Calls the specified function for each of the Records in the cache.
13743 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13744 * Returning <em>false</em> aborts and exits the iteration.
13745 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13747 each : function(fn, scope){
13748 this.data.each(fn, scope);
13752 * Gets all records modified since the last commit. Modified records are persisted across load operations
13753 * (e.g., during paging).
13754 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13756 getModifiedRecords : function(){
13757 return this.modified;
13761 createFilterFn : function(property, value, anyMatch){
13762 if(!value.exec){ // not a regex
13763 value = String(value);
13764 if(value.length == 0){
13767 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13769 return function(r){
13770 return value.test(r.data[property]);
13775 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13776 * @param {String} property A field on your records
13777 * @param {Number} start The record index to start at (defaults to 0)
13778 * @param {Number} end The last record index to include (defaults to length - 1)
13779 * @return {Number} The sum
13781 sum : function(property, start, end){
13782 var rs = this.data.items, v = 0;
13783 start = start || 0;
13784 end = (end || end === 0) ? end : rs.length-1;
13786 for(var i = start; i <= end; i++){
13787 v += (rs[i].data[property] || 0);
13793 * Filter the records by a specified property.
13794 * @param {String} field A field on your records
13795 * @param {String/RegExp} value Either a string that the field
13796 * should start with or a RegExp to test against the field
13797 * @param {Boolean} anyMatch True to match any part not just the beginning
13799 filter : function(property, value, anyMatch){
13800 var fn = this.createFilterFn(property, value, anyMatch);
13801 return fn ? this.filterBy(fn) : this.clearFilter();
13805 * Filter by a function. The specified function will be called with each
13806 * record in this data source. If the function returns true the record is included,
13807 * otherwise it is filtered.
13808 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13809 * @param {Object} scope (optional) The scope of the function (defaults to this)
13811 filterBy : function(fn, scope){
13812 this.snapshot = this.snapshot || this.data;
13813 this.data = this.queryBy(fn, scope||this);
13814 this.fireEvent("datachanged", this);
13818 * Query the records by a specified property.
13819 * @param {String} field A field on your records
13820 * @param {String/RegExp} value Either a string that the field
13821 * should start with or a RegExp to test against the field
13822 * @param {Boolean} anyMatch True to match any part not just the beginning
13823 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13825 query : function(property, value, anyMatch){
13826 var fn = this.createFilterFn(property, value, anyMatch);
13827 return fn ? this.queryBy(fn) : this.data.clone();
13831 * Query by a function. The specified function will be called with each
13832 * record in this data source. If the function returns true the record is included
13834 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13835 * @param {Object} scope (optional) The scope of the function (defaults to this)
13836 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13838 queryBy : function(fn, scope){
13839 var data = this.snapshot || this.data;
13840 return data.filterBy(fn, scope||this);
13844 * Collects unique values for a particular dataIndex from this store.
13845 * @param {String} dataIndex The property to collect
13846 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13847 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13848 * @return {Array} An array of the unique values
13850 collect : function(dataIndex, allowNull, bypassFilter){
13851 var d = (bypassFilter === true && this.snapshot) ?
13852 this.snapshot.items : this.data.items;
13853 var v, sv, r = [], l = {};
13854 for(var i = 0, len = d.length; i < len; i++){
13855 v = d[i].data[dataIndex];
13857 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13866 * Revert to a view of the Record cache with no filtering applied.
13867 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13869 clearFilter : function(suppressEvent){
13870 if(this.snapshot && this.snapshot != this.data){
13871 this.data = this.snapshot;
13872 delete this.snapshot;
13873 if(suppressEvent !== true){
13874 this.fireEvent("datachanged", this);
13880 afterEdit : function(record){
13881 if(this.modified.indexOf(record) == -1){
13882 this.modified.push(record);
13884 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13888 afterReject : function(record){
13889 this.modified.remove(record);
13890 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13894 afterCommit : function(record){
13895 this.modified.remove(record);
13896 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13900 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13901 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13903 commitChanges : function(){
13904 var m = this.modified.slice(0);
13905 this.modified = [];
13906 for(var i = 0, len = m.length; i < len; i++){
13912 * Cancel outstanding changes on all changed records.
13914 rejectChanges : function(){
13915 var m = this.modified.slice(0);
13916 this.modified = [];
13917 for(var i = 0, len = m.length; i < len; i++){
13922 onMetaChange : function(meta, rtype, o){
13923 this.recordType = rtype;
13924 this.fields = rtype.prototype.fields;
13925 delete this.snapshot;
13926 this.sortInfo = meta.sortInfo || this.sortInfo;
13927 this.modified = [];
13928 this.fireEvent('metachange', this, this.reader.meta);
13931 moveIndex : function(data, type)
13933 var index = this.indexOf(data);
13935 var newIndex = index + type;
13939 this.insert(newIndex, data);
13944 * Ext JS Library 1.1.1
13945 * Copyright(c) 2006-2007, Ext JS, LLC.
13947 * Originally Released Under LGPL - original licence link has changed is not relivant.
13950 * <script type="text/javascript">
13954 * @class Roo.data.SimpleStore
13955 * @extends Roo.data.Store
13956 * Small helper class to make creating Stores from Array data easier.
13957 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13958 * @cfg {Array} fields An array of field definition objects, or field name strings.
13959 * @cfg {Object} an existing reader (eg. copied from another store)
13960 * @cfg {Array} data The multi-dimensional array of data
13962 * @param {Object} config
13964 Roo.data.SimpleStore = function(config)
13966 Roo.data.SimpleStore.superclass.constructor.call(this, {
13968 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13971 Roo.data.Record.create(config.fields)
13973 proxy : new Roo.data.MemoryProxy(config.data)
13977 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13979 * Ext JS Library 1.1.1
13980 * Copyright(c) 2006-2007, Ext JS, LLC.
13982 * Originally Released Under LGPL - original licence link has changed is not relivant.
13985 * <script type="text/javascript">
13990 * @extends Roo.data.Store
13991 * @class Roo.data.JsonStore
13992 * Small helper class to make creating Stores for JSON data easier. <br/>
13994 var store = new Roo.data.JsonStore({
13995 url: 'get-images.php',
13997 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14000 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14001 * JsonReader and HttpProxy (unless inline data is provided).</b>
14002 * @cfg {Array} fields An array of field definition objects, or field name strings.
14004 * @param {Object} config
14006 Roo.data.JsonStore = function(c){
14007 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14008 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14009 reader: new Roo.data.JsonReader(c, c.fields)
14012 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14014 * Ext JS Library 1.1.1
14015 * Copyright(c) 2006-2007, Ext JS, LLC.
14017 * Originally Released Under LGPL - original licence link has changed is not relivant.
14020 * <script type="text/javascript">
14024 Roo.data.Field = function(config){
14025 if(typeof config == "string"){
14026 config = {name: config};
14028 Roo.apply(this, config);
14031 this.type = "auto";
14034 var st = Roo.data.SortTypes;
14035 // named sortTypes are supported, here we look them up
14036 if(typeof this.sortType == "string"){
14037 this.sortType = st[this.sortType];
14040 // set default sortType for strings and dates
14041 if(!this.sortType){
14044 this.sortType = st.asUCString;
14047 this.sortType = st.asDate;
14050 this.sortType = st.none;
14055 var stripRe = /[\$,%]/g;
14057 // prebuilt conversion function for this field, instead of
14058 // switching every time we're reading a value
14060 var cv, dateFormat = this.dateFormat;
14065 cv = function(v){ return v; };
14068 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14072 return v !== undefined && v !== null && v !== '' ?
14073 parseInt(String(v).replace(stripRe, ""), 10) : '';
14078 return v !== undefined && v !== null && v !== '' ?
14079 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14084 cv = function(v){ return v === true || v === "true" || v == 1; };
14091 if(v instanceof Date){
14095 if(dateFormat == "timestamp"){
14096 return new Date(v*1000);
14098 return Date.parseDate(v, dateFormat);
14100 var parsed = Date.parse(v);
14101 return parsed ? new Date(parsed) : null;
14110 Roo.data.Field.prototype = {
14118 * Ext JS Library 1.1.1
14119 * Copyright(c) 2006-2007, Ext JS, LLC.
14121 * Originally Released Under LGPL - original licence link has changed is not relivant.
14124 * <script type="text/javascript">
14127 // Base class for reading structured data from a data source. This class is intended to be
14128 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14131 * @class Roo.data.DataReader
14132 * Base class for reading structured data from a data source. This class is intended to be
14133 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14136 Roo.data.DataReader = function(meta, recordType){
14140 this.recordType = recordType instanceof Array ?
14141 Roo.data.Record.create(recordType) : recordType;
14144 Roo.data.DataReader.prototype = {
14147 readerType : 'Data',
14149 * Create an empty record
14150 * @param {Object} data (optional) - overlay some values
14151 * @return {Roo.data.Record} record created.
14153 newRow : function(d) {
14155 this.recordType.prototype.fields.each(function(c) {
14157 case 'int' : da[c.name] = 0; break;
14158 case 'date' : da[c.name] = new Date(); break;
14159 case 'float' : da[c.name] = 0.0; break;
14160 case 'boolean' : da[c.name] = false; break;
14161 default : da[c.name] = ""; break;
14165 return new this.recordType(Roo.apply(da, d));
14171 * Ext JS Library 1.1.1
14172 * Copyright(c) 2006-2007, Ext JS, LLC.
14174 * Originally Released Under LGPL - original licence link has changed is not relivant.
14177 * <script type="text/javascript">
14181 * @class Roo.data.DataProxy
14182 * @extends Roo.data.Observable
14183 * This class is an abstract base class for implementations which provide retrieval of
14184 * unformatted data objects.<br>
14186 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14187 * (of the appropriate type which knows how to parse the data object) to provide a block of
14188 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14190 * Custom implementations must implement the load method as described in
14191 * {@link Roo.data.HttpProxy#load}.
14193 Roo.data.DataProxy = function(){
14196 * @event beforeload
14197 * Fires before a network request is made to retrieve a data object.
14198 * @param {Object} This DataProxy object.
14199 * @param {Object} params The params parameter to the load function.
14204 * Fires before the load method's callback is called.
14205 * @param {Object} This DataProxy object.
14206 * @param {Object} o The data object.
14207 * @param {Object} arg The callback argument object passed to the load function.
14211 * @event loadexception
14212 * Fires if an Exception occurs during data retrieval.
14213 * @param {Object} This DataProxy object.
14214 * @param {Object} o The data object.
14215 * @param {Object} arg The callback argument object passed to the load function.
14216 * @param {Object} e The Exception.
14218 loadexception : true
14220 Roo.data.DataProxy.superclass.constructor.call(this);
14223 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14226 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14230 * Ext JS Library 1.1.1
14231 * Copyright(c) 2006-2007, Ext JS, LLC.
14233 * Originally Released Under LGPL - original licence link has changed is not relivant.
14236 * <script type="text/javascript">
14239 * @class Roo.data.MemoryProxy
14240 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14241 * to the Reader when its load method is called.
14243 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14245 Roo.data.MemoryProxy = function(data){
14249 Roo.data.MemoryProxy.superclass.constructor.call(this);
14253 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14256 * Load data from the requested source (in this case an in-memory
14257 * data object passed to the constructor), read the data object into
14258 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14259 * process that block using the passed callback.
14260 * @param {Object} params This parameter is not used by the MemoryProxy class.
14261 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14262 * object into a block of Roo.data.Records.
14263 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14264 * The function must be passed <ul>
14265 * <li>The Record block object</li>
14266 * <li>The "arg" argument from the load function</li>
14267 * <li>A boolean success indicator</li>
14269 * @param {Object} scope The scope in which to call the callback
14270 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14272 load : function(params, reader, callback, scope, arg){
14273 params = params || {};
14276 result = reader.readRecords(params.data ? params.data :this.data);
14278 this.fireEvent("loadexception", this, arg, null, e);
14279 callback.call(scope, null, arg, false);
14282 callback.call(scope, result, arg, true);
14286 update : function(params, records){
14291 * Ext JS Library 1.1.1
14292 * Copyright(c) 2006-2007, Ext JS, LLC.
14294 * Originally Released Under LGPL - original licence link has changed is not relivant.
14297 * <script type="text/javascript">
14300 * @class Roo.data.HttpProxy
14301 * @extends Roo.data.DataProxy
14302 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14303 * configured to reference a certain URL.<br><br>
14305 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14306 * from which the running page was served.<br><br>
14308 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14310 * Be aware that to enable the browser to parse an XML document, the server must set
14311 * the Content-Type header in the HTTP response to "text/xml".
14313 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14314 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14315 * will be used to make the request.
14317 Roo.data.HttpProxy = function(conn){
14318 Roo.data.HttpProxy.superclass.constructor.call(this);
14319 // is conn a conn config or a real conn?
14321 this.useAjax = !conn || !conn.events;
14325 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14326 // thse are take from connection...
14329 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14332 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14333 * extra parameters to each request made by this object. (defaults to undefined)
14336 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14337 * to each request made by this object. (defaults to undefined)
14340 * @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)
14343 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14346 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14352 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14356 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14357 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14358 * a finer-grained basis than the DataProxy events.
14360 getConnection : function(){
14361 return this.useAjax ? Roo.Ajax : this.conn;
14365 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14366 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14367 * process that block using the passed callback.
14368 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14369 * for the request to the remote server.
14370 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14371 * object into a block of Roo.data.Records.
14372 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14373 * The function must be passed <ul>
14374 * <li>The Record block object</li>
14375 * <li>The "arg" argument from the load function</li>
14376 * <li>A boolean success indicator</li>
14378 * @param {Object} scope The scope in which to call the callback
14379 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14381 load : function(params, reader, callback, scope, arg){
14382 if(this.fireEvent("beforeload", this, params) !== false){
14384 params : params || {},
14386 callback : callback,
14391 callback : this.loadResponse,
14395 Roo.applyIf(o, this.conn);
14396 if(this.activeRequest){
14397 Roo.Ajax.abort(this.activeRequest);
14399 this.activeRequest = Roo.Ajax.request(o);
14401 this.conn.request(o);
14404 callback.call(scope||this, null, arg, false);
14409 loadResponse : function(o, success, response){
14410 delete this.activeRequest;
14412 this.fireEvent("loadexception", this, o, response);
14413 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14418 result = o.reader.read(response);
14420 this.fireEvent("loadexception", this, o, response, e);
14421 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14425 this.fireEvent("load", this, o, o.request.arg);
14426 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14430 update : function(dataSet){
14435 updateResponse : function(dataSet){
14440 * Ext JS Library 1.1.1
14441 * Copyright(c) 2006-2007, Ext JS, LLC.
14443 * Originally Released Under LGPL - original licence link has changed is not relivant.
14446 * <script type="text/javascript">
14450 * @class Roo.data.ScriptTagProxy
14451 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14452 * other than the originating domain of the running page.<br><br>
14454 * <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
14455 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14457 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14458 * source code that is used as the source inside a <script> tag.<br><br>
14460 * In order for the browser to process the returned data, the server must wrap the data object
14461 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14462 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14463 * depending on whether the callback name was passed:
14466 boolean scriptTag = false;
14467 String cb = request.getParameter("callback");
14470 response.setContentType("text/javascript");
14472 response.setContentType("application/x-json");
14474 Writer out = response.getWriter();
14476 out.write(cb + "(");
14478 out.print(dataBlock.toJsonString());
14485 * @param {Object} config A configuration object.
14487 Roo.data.ScriptTagProxy = function(config){
14488 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14489 Roo.apply(this, config);
14490 this.head = document.getElementsByTagName("head")[0];
14493 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14495 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14497 * @cfg {String} url The URL from which to request the data object.
14500 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14504 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14505 * the server the name of the callback function set up by the load call to process the returned data object.
14506 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14507 * javascript output which calls this named function passing the data object as its only parameter.
14509 callbackParam : "callback",
14511 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14512 * name to the request.
14517 * Load data from the configured URL, read the data object into
14518 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14519 * process that block using the passed callback.
14520 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14521 * for the request to the remote server.
14522 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14523 * object into a block of Roo.data.Records.
14524 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14525 * The function must be passed <ul>
14526 * <li>The Record block object</li>
14527 * <li>The "arg" argument from the load function</li>
14528 * <li>A boolean success indicator</li>
14530 * @param {Object} scope The scope in which to call the callback
14531 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14533 load : function(params, reader, callback, scope, arg){
14534 if(this.fireEvent("beforeload", this, params) !== false){
14536 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14538 var url = this.url;
14539 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14541 url += "&_dc=" + (new Date().getTime());
14543 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14546 cb : "stcCallback"+transId,
14547 scriptId : "stcScript"+transId,
14551 callback : callback,
14557 window[trans.cb] = function(o){
14558 conn.handleResponse(o, trans);
14561 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14563 if(this.autoAbort !== false){
14567 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14569 var script = document.createElement("script");
14570 script.setAttribute("src", url);
14571 script.setAttribute("type", "text/javascript");
14572 script.setAttribute("id", trans.scriptId);
14573 this.head.appendChild(script);
14575 this.trans = trans;
14577 callback.call(scope||this, null, arg, false);
14582 isLoading : function(){
14583 return this.trans ? true : false;
14587 * Abort the current server request.
14589 abort : function(){
14590 if(this.isLoading()){
14591 this.destroyTrans(this.trans);
14596 destroyTrans : function(trans, isLoaded){
14597 this.head.removeChild(document.getElementById(trans.scriptId));
14598 clearTimeout(trans.timeoutId);
14600 window[trans.cb] = undefined;
14602 delete window[trans.cb];
14605 // if hasn't been loaded, wait for load to remove it to prevent script error
14606 window[trans.cb] = function(){
14607 window[trans.cb] = undefined;
14609 delete window[trans.cb];
14616 handleResponse : function(o, trans){
14617 this.trans = false;
14618 this.destroyTrans(trans, true);
14621 result = trans.reader.readRecords(o);
14623 this.fireEvent("loadexception", this, o, trans.arg, e);
14624 trans.callback.call(trans.scope||window, null, trans.arg, false);
14627 this.fireEvent("load", this, o, trans.arg);
14628 trans.callback.call(trans.scope||window, result, trans.arg, true);
14632 handleFailure : function(trans){
14633 this.trans = false;
14634 this.destroyTrans(trans, false);
14635 this.fireEvent("loadexception", this, null, trans.arg);
14636 trans.callback.call(trans.scope||window, null, trans.arg, false);
14640 * Ext JS Library 1.1.1
14641 * Copyright(c) 2006-2007, Ext JS, LLC.
14643 * Originally Released Under LGPL - original licence link has changed is not relivant.
14646 * <script type="text/javascript">
14650 * @class Roo.data.JsonReader
14651 * @extends Roo.data.DataReader
14652 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14653 * based on mappings in a provided Roo.data.Record constructor.
14655 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14656 * in the reply previously.
14661 var RecordDef = Roo.data.Record.create([
14662 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14663 {name: 'occupation'} // This field will use "occupation" as the mapping.
14665 var myReader = new Roo.data.JsonReader({
14666 totalProperty: "results", // The property which contains the total dataset size (optional)
14667 root: "rows", // The property which contains an Array of row objects
14668 id: "id" // The property within each row object that provides an ID for the record (optional)
14672 * This would consume a JSON file like this:
14674 { 'results': 2, 'rows': [
14675 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14676 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14679 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14680 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14681 * paged from the remote server.
14682 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14683 * @cfg {String} root name of the property which contains the Array of row objects.
14684 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14685 * @cfg {Array} fields Array of field definition objects
14687 * Create a new JsonReader
14688 * @param {Object} meta Metadata configuration options
14689 * @param {Object} recordType Either an Array of field definition objects,
14690 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14692 Roo.data.JsonReader = function(meta, recordType){
14695 // set some defaults:
14696 Roo.applyIf(meta, {
14697 totalProperty: 'total',
14698 successProperty : 'success',
14703 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14705 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14707 readerType : 'Json',
14710 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14711 * Used by Store query builder to append _requestMeta to params.
14714 metaFromRemote : false,
14716 * This method is only used by a DataProxy which has retrieved data from a remote server.
14717 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14718 * @return {Object} data A data block which is used by an Roo.data.Store object as
14719 * a cache of Roo.data.Records.
14721 read : function(response){
14722 var json = response.responseText;
14724 var o = /* eval:var:o */ eval("("+json+")");
14726 throw {message: "JsonReader.read: Json object not found"};
14732 this.metaFromRemote = true;
14733 this.meta = o.metaData;
14734 this.recordType = Roo.data.Record.create(o.metaData.fields);
14735 this.onMetaChange(this.meta, this.recordType, o);
14737 return this.readRecords(o);
14740 // private function a store will implement
14741 onMetaChange : function(meta, recordType, o){
14748 simpleAccess: function(obj, subsc) {
14755 getJsonAccessor: function(){
14757 return function(expr) {
14759 return(re.test(expr))
14760 ? new Function("obj", "return obj." + expr)
14765 return Roo.emptyFn;
14770 * Create a data block containing Roo.data.Records from an XML document.
14771 * @param {Object} o An object which contains an Array of row objects in the property specified
14772 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14773 * which contains the total size of the dataset.
14774 * @return {Object} data A data block which is used by an Roo.data.Store object as
14775 * a cache of Roo.data.Records.
14777 readRecords : function(o){
14779 * After any data loads, the raw JSON data is available for further custom processing.
14783 var s = this.meta, Record = this.recordType,
14784 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14786 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14788 if(s.totalProperty) {
14789 this.getTotal = this.getJsonAccessor(s.totalProperty);
14791 if(s.successProperty) {
14792 this.getSuccess = this.getJsonAccessor(s.successProperty);
14794 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14796 var g = this.getJsonAccessor(s.id);
14797 this.getId = function(rec) {
14799 return (r === undefined || r === "") ? null : r;
14802 this.getId = function(){return null;};
14805 for(var jj = 0; jj < fl; jj++){
14807 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14808 this.ef[jj] = this.getJsonAccessor(map);
14812 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14813 if(s.totalProperty){
14814 var vt = parseInt(this.getTotal(o), 10);
14819 if(s.successProperty){
14820 var vs = this.getSuccess(o);
14821 if(vs === false || vs === 'false'){
14826 for(var i = 0; i < c; i++){
14829 var id = this.getId(n);
14830 for(var j = 0; j < fl; j++){
14832 var v = this.ef[j](n);
14834 Roo.log('missing convert for ' + f.name);
14838 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14840 var record = new Record(values, id);
14842 records[i] = record;
14848 totalRecords : totalRecords
14851 // used when loading children.. @see loadDataFromChildren
14852 toLoadData: function(rec)
14854 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14855 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14856 return { data : data, total : data.length };
14861 * Ext JS Library 1.1.1
14862 * Copyright(c) 2006-2007, Ext JS, LLC.
14864 * Originally Released Under LGPL - original licence link has changed is not relivant.
14867 * <script type="text/javascript">
14871 * @class Roo.data.ArrayReader
14872 * @extends Roo.data.DataReader
14873 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14874 * Each element of that Array represents a row of data fields. The
14875 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14876 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14880 var RecordDef = Roo.data.Record.create([
14881 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14882 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14884 var myReader = new Roo.data.ArrayReader({
14885 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14889 * This would consume an Array like this:
14891 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14895 * Create a new JsonReader
14896 * @param {Object} meta Metadata configuration options.
14897 * @param {Object|Array} recordType Either an Array of field definition objects
14899 * @cfg {Array} fields Array of field definition objects
14900 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14901 * as specified to {@link Roo.data.Record#create},
14902 * or an {@link Roo.data.Record} object
14905 * created using {@link Roo.data.Record#create}.
14907 Roo.data.ArrayReader = function(meta, recordType)
14909 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14912 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14915 * Create a data block containing Roo.data.Records from an XML document.
14916 * @param {Object} o An Array of row objects which represents the dataset.
14917 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14918 * a cache of Roo.data.Records.
14920 readRecords : function(o)
14922 var sid = this.meta ? this.meta.id : null;
14923 var recordType = this.recordType, fields = recordType.prototype.fields;
14926 for(var i = 0; i < root.length; i++){
14929 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14930 for(var j = 0, jlen = fields.length; j < jlen; j++){
14931 var f = fields.items[j];
14932 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14933 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14935 values[f.name] = v;
14937 var record = new recordType(values, id);
14939 records[records.length] = record;
14943 totalRecords : records.length
14946 // used when loading children.. @see loadDataFromChildren
14947 toLoadData: function(rec)
14949 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14950 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14961 * @class Roo.bootstrap.ComboBox
14962 * @extends Roo.bootstrap.TriggerField
14963 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14964 * @cfg {Boolean} append (true|false) default false
14965 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14966 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14967 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14968 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14969 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14970 * @cfg {Boolean} animate default true
14971 * @cfg {Boolean} emptyResultText only for touch device
14972 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14973 * @cfg {String} emptyTitle default ''
14974 * @cfg {Number} width fixed with? experimental
14976 * Create a new ComboBox.
14977 * @param {Object} config Configuration options
14979 Roo.bootstrap.ComboBox = function(config){
14980 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14984 * Fires when the dropdown list is expanded
14985 * @param {Roo.bootstrap.ComboBox} combo This combo box
14990 * Fires when the dropdown list is collapsed
14991 * @param {Roo.bootstrap.ComboBox} combo This combo box
14995 * @event beforeselect
14996 * Fires before a list item is selected. Return false to cancel the selection.
14997 * @param {Roo.bootstrap.ComboBox} combo This combo box
14998 * @param {Roo.data.Record} record The data record returned from the underlying store
14999 * @param {Number} index The index of the selected item in the dropdown list
15001 'beforeselect' : true,
15004 * Fires when a list item is selected
15005 * @param {Roo.bootstrap.ComboBox} combo This combo box
15006 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15007 * @param {Number} index The index of the selected item in the dropdown list
15011 * @event beforequery
15012 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15013 * The event object passed has these properties:
15014 * @param {Roo.bootstrap.ComboBox} combo This combo box
15015 * @param {String} query The query
15016 * @param {Boolean} forceAll true to force "all" query
15017 * @param {Boolean} cancel true to cancel the query
15018 * @param {Object} e The query event object
15020 'beforequery': true,
15023 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15024 * @param {Roo.bootstrap.ComboBox} combo This combo box
15029 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15030 * @param {Roo.bootstrap.ComboBox} combo This combo box
15031 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15036 * Fires when the remove value from the combobox array
15037 * @param {Roo.bootstrap.ComboBox} combo This combo box
15041 * @event afterremove
15042 * Fires when the remove value from the combobox array
15043 * @param {Roo.bootstrap.ComboBox} combo This combo box
15045 'afterremove' : true,
15047 * @event specialfilter
15048 * Fires when specialfilter
15049 * @param {Roo.bootstrap.ComboBox} combo This combo box
15051 'specialfilter' : true,
15054 * Fires when tick the element
15055 * @param {Roo.bootstrap.ComboBox} combo This combo box
15059 * @event touchviewdisplay
15060 * Fires when touch view require special display (default is using displayField)
15061 * @param {Roo.bootstrap.ComboBox} combo This combo box
15062 * @param {Object} cfg set html .
15064 'touchviewdisplay' : true
15069 this.tickItems = [];
15071 this.selectedIndex = -1;
15072 if(this.mode == 'local'){
15073 if(config.queryDelay === undefined){
15074 this.queryDelay = 10;
15076 if(config.minChars === undefined){
15082 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15085 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15086 * rendering into an Roo.Editor, defaults to false)
15089 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15090 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15093 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15096 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15097 * the dropdown list (defaults to undefined, with no header element)
15101 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15105 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15107 listWidth: undefined,
15109 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15110 * mode = 'remote' or 'text' if mode = 'local')
15112 displayField: undefined,
15115 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15116 * mode = 'remote' or 'value' if mode = 'local').
15117 * Note: use of a valueField requires the user make a selection
15118 * in order for a value to be mapped.
15120 valueField: undefined,
15122 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15127 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15128 * field's data value (defaults to the underlying DOM element's name)
15130 hiddenName: undefined,
15132 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15136 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15138 selectedClass: 'active',
15141 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15145 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15146 * anchor positions (defaults to 'tl-bl')
15148 listAlign: 'tl-bl?',
15150 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15154 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15155 * query specified by the allQuery config option (defaults to 'query')
15157 triggerAction: 'query',
15159 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15160 * (defaults to 4, does not apply if editable = false)
15164 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15165 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15169 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15170 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15174 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15175 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15179 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15180 * when editable = true (defaults to false)
15182 selectOnFocus:false,
15184 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15186 queryParam: 'query',
15188 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15189 * when mode = 'remote' (defaults to 'Loading...')
15191 loadingText: 'Loading...',
15193 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15197 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15201 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15202 * traditional select (defaults to true)
15206 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15210 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15214 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15215 * listWidth has a higher value)
15219 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15220 * allow the user to set arbitrary text into the field (defaults to false)
15222 forceSelection:false,
15224 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15225 * if typeAhead = true (defaults to 250)
15227 typeAheadDelay : 250,
15229 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15230 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15232 valueNotFoundText : undefined,
15234 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15236 blockFocus : false,
15239 * @cfg {Boolean} disableClear Disable showing of clear button.
15241 disableClear : false,
15243 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15245 alwaysQuery : false,
15248 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15253 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15255 invalidClass : "has-warning",
15258 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15260 validClass : "has-success",
15263 * @cfg {Boolean} specialFilter (true|false) special filter default false
15265 specialFilter : false,
15268 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15270 mobileTouchView : true,
15273 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15275 useNativeIOS : false,
15278 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15280 mobile_restrict_height : false,
15282 ios_options : false,
15294 btnPosition : 'right',
15295 triggerList : true,
15296 showToggleBtn : true,
15298 emptyResultText: 'Empty',
15299 triggerText : 'Select',
15303 // element that contains real text value.. (when hidden is used..)
15305 getAutoCreate : function()
15310 * Render classic select for iso
15313 if(Roo.isIOS && this.useNativeIOS){
15314 cfg = this.getAutoCreateNativeIOS();
15322 if(Roo.isTouch && this.mobileTouchView){
15323 cfg = this.getAutoCreateTouchView();
15330 if(!this.tickable){
15331 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15336 * ComboBox with tickable selections
15339 var align = this.labelAlign || this.parentLabelAlign();
15342 cls : 'form-group roo-combobox-tickable' //input-group
15345 var btn_text_select = '';
15346 var btn_text_done = '';
15347 var btn_text_cancel = '';
15349 if (this.btn_text_show) {
15350 btn_text_select = 'Select';
15351 btn_text_done = 'Done';
15352 btn_text_cancel = 'Cancel';
15357 cls : 'tickable-buttons',
15362 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15363 //html : this.triggerText
15364 html: btn_text_select
15370 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15372 html: btn_text_done
15378 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15380 html: btn_text_cancel
15386 buttons.cn.unshift({
15388 cls: 'roo-select2-search-field-input'
15394 Roo.each(buttons.cn, function(c){
15396 c.cls += ' btn-' + _this.size;
15399 if (_this.disabled) {
15406 style : 'display: contents',
15411 cls: 'form-hidden-field'
15415 cls: 'roo-select2-choices',
15419 cls: 'roo-select2-search-field',
15430 cls: 'roo-select2-container input-group roo-select2-container-multi',
15436 // cls: 'typeahead typeahead-long dropdown-menu',
15437 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15442 if(this.hasFeedback && !this.allowBlank){
15446 cls: 'glyphicon form-control-feedback'
15449 combobox.cn.push(feedback);
15456 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15457 tooltip : 'This field is required'
15459 if (Roo.bootstrap.version == 4) {
15462 style : 'display:none'
15465 if (align ==='left' && this.fieldLabel.length) {
15467 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15474 cls : 'control-label col-form-label',
15475 html : this.fieldLabel
15487 var labelCfg = cfg.cn[1];
15488 var contentCfg = cfg.cn[2];
15491 if(this.indicatorpos == 'right'){
15497 cls : 'control-label col-form-label',
15501 html : this.fieldLabel
15517 labelCfg = cfg.cn[0];
15518 contentCfg = cfg.cn[1];
15522 if(this.labelWidth > 12){
15523 labelCfg.style = "width: " + this.labelWidth + 'px';
15525 if(this.width * 1 > 0){
15526 contentCfg.style = "width: " + this.width + 'px';
15528 if(this.labelWidth < 13 && this.labelmd == 0){
15529 this.labelmd = this.labelWidth;
15532 if(this.labellg > 0){
15533 labelCfg.cls += ' col-lg-' + this.labellg;
15534 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15537 if(this.labelmd > 0){
15538 labelCfg.cls += ' col-md-' + this.labelmd;
15539 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15542 if(this.labelsm > 0){
15543 labelCfg.cls += ' col-sm-' + this.labelsm;
15544 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15547 if(this.labelxs > 0){
15548 labelCfg.cls += ' col-xs-' + this.labelxs;
15549 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15553 } else if ( this.fieldLabel.length) {
15554 // Roo.log(" label");
15559 //cls : 'input-group-addon',
15560 html : this.fieldLabel
15565 if(this.indicatorpos == 'right'){
15569 //cls : 'input-group-addon',
15570 html : this.fieldLabel
15580 // Roo.log(" no label && no align");
15587 ['xs','sm','md','lg'].map(function(size){
15588 if (settings[size]) {
15589 cfg.cls += ' col-' + size + '-' + settings[size];
15597 _initEventsCalled : false,
15600 initEvents: function()
15602 if (this._initEventsCalled) { // as we call render... prevent looping...
15605 this._initEventsCalled = true;
15608 throw "can not find store for combo";
15611 this.indicator = this.indicatorEl();
15613 this.store = Roo.factory(this.store, Roo.data);
15614 this.store.parent = this;
15616 // if we are building from html. then this element is so complex, that we can not really
15617 // use the rendered HTML.
15618 // so we have to trash and replace the previous code.
15619 if (Roo.XComponent.build_from_html) {
15620 // remove this element....
15621 var e = this.el.dom, k=0;
15622 while (e ) { e = e.previousSibling; ++k;}
15627 this.rendered = false;
15629 this.render(this.parent().getChildContainer(true), k);
15632 if(Roo.isIOS && this.useNativeIOS){
15633 this.initIOSView();
15641 if(Roo.isTouch && this.mobileTouchView){
15642 this.initTouchView();
15647 this.initTickableEvents();
15651 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15653 if(this.hiddenName){
15655 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15657 this.hiddenField.dom.value =
15658 this.hiddenValue !== undefined ? this.hiddenValue :
15659 this.value !== undefined ? this.value : '';
15661 // prevent input submission
15662 this.el.dom.removeAttribute('name');
15663 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15668 // this.el.dom.setAttribute('autocomplete', 'off');
15671 var cls = 'x-combo-list';
15673 //this.list = new Roo.Layer({
15674 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15680 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15681 _this.list.setWidth(lw);
15684 this.list.on('mouseover', this.onViewOver, this);
15685 this.list.on('mousemove', this.onViewMove, this);
15686 this.list.on('scroll', this.onViewScroll, this);
15689 this.list.swallowEvent('mousewheel');
15690 this.assetHeight = 0;
15693 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15694 this.assetHeight += this.header.getHeight();
15697 this.innerList = this.list.createChild({cls:cls+'-inner'});
15698 this.innerList.on('mouseover', this.onViewOver, this);
15699 this.innerList.on('mousemove', this.onViewMove, this);
15700 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15702 if(this.allowBlank && !this.pageSize && !this.disableClear){
15703 this.footer = this.list.createChild({cls:cls+'-ft'});
15704 this.pageTb = new Roo.Toolbar(this.footer);
15708 this.footer = this.list.createChild({cls:cls+'-ft'});
15709 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15710 {pageSize: this.pageSize});
15714 if (this.pageTb && this.allowBlank && !this.disableClear) {
15716 this.pageTb.add(new Roo.Toolbar.Fill(), {
15717 cls: 'x-btn-icon x-btn-clear',
15719 handler: function()
15722 _this.clearValue();
15723 _this.onSelect(false, -1);
15728 this.assetHeight += this.footer.getHeight();
15733 this.tpl = Roo.bootstrap.version == 4 ?
15734 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15735 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15738 this.view = new Roo.View(this.list, this.tpl, {
15739 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15741 //this.view.wrapEl.setDisplayed(false);
15742 this.view.on('click', this.onViewClick, this);
15745 this.store.on('beforeload', this.onBeforeLoad, this);
15746 this.store.on('load', this.onLoad, this);
15747 this.store.on('loadexception', this.onLoadException, this);
15749 if(this.resizable){
15750 this.resizer = new Roo.Resizable(this.list, {
15751 pinned:true, handles:'se'
15753 this.resizer.on('resize', function(r, w, h){
15754 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15755 this.listWidth = w;
15756 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15757 this.restrictHeight();
15759 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15762 if(!this.editable){
15763 this.editable = true;
15764 this.setEditable(false);
15769 if (typeof(this.events.add.listeners) != 'undefined') {
15771 this.addicon = this.wrap.createChild(
15772 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15774 this.addicon.on('click', function(e) {
15775 this.fireEvent('add', this);
15778 if (typeof(this.events.edit.listeners) != 'undefined') {
15780 this.editicon = this.wrap.createChild(
15781 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15782 if (this.addicon) {
15783 this.editicon.setStyle('margin-left', '40px');
15785 this.editicon.on('click', function(e) {
15787 // we fire even if inothing is selected..
15788 this.fireEvent('edit', this, this.lastData );
15794 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15795 "up" : function(e){
15796 this.inKeyMode = true;
15800 "down" : function(e){
15801 if(!this.isExpanded()){
15802 this.onTriggerClick();
15804 this.inKeyMode = true;
15809 "enter" : function(e){
15810 // this.onViewClick();
15814 if(this.fireEvent("specialkey", this, e)){
15815 this.onViewClick(false);
15821 "esc" : function(e){
15825 "tab" : function(e){
15828 if(this.fireEvent("specialkey", this, e)){
15829 this.onViewClick(false);
15837 doRelay : function(foo, bar, hname){
15838 if(hname == 'down' || this.scope.isExpanded()){
15839 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15848 this.queryDelay = Math.max(this.queryDelay || 10,
15849 this.mode == 'local' ? 10 : 250);
15852 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15854 if(this.typeAhead){
15855 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15857 if(this.editable !== false){
15858 this.inputEl().on("keyup", this.onKeyUp, this);
15860 if(this.forceSelection){
15861 this.inputEl().on('blur', this.doForce, this);
15865 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15866 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15870 initTickableEvents: function()
15874 if(this.hiddenName){
15876 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15878 this.hiddenField.dom.value =
15879 this.hiddenValue !== undefined ? this.hiddenValue :
15880 this.value !== undefined ? this.value : '';
15882 // prevent input submission
15883 this.el.dom.removeAttribute('name');
15884 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15889 // this.list = this.el.select('ul.dropdown-menu',true).first();
15891 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15892 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15893 if(this.triggerList){
15894 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15897 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15898 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15900 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15901 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15903 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15904 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15906 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15907 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15908 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15911 this.cancelBtn.hide();
15916 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15917 _this.list.setWidth(lw);
15920 this.list.on('mouseover', this.onViewOver, this);
15921 this.list.on('mousemove', this.onViewMove, this);
15923 this.list.on('scroll', this.onViewScroll, this);
15926 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15927 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15930 this.view = new Roo.View(this.list, this.tpl, {
15935 selectedClass: this.selectedClass
15938 //this.view.wrapEl.setDisplayed(false);
15939 this.view.on('click', this.onViewClick, this);
15943 this.store.on('beforeload', this.onBeforeLoad, this);
15944 this.store.on('load', this.onLoad, this);
15945 this.store.on('loadexception', this.onLoadException, this);
15948 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15949 "up" : function(e){
15950 this.inKeyMode = true;
15954 "down" : function(e){
15955 this.inKeyMode = true;
15959 "enter" : function(e){
15960 if(this.fireEvent("specialkey", this, e)){
15961 this.onViewClick(false);
15967 "esc" : function(e){
15968 this.onTickableFooterButtonClick(e, false, false);
15971 "tab" : function(e){
15972 this.fireEvent("specialkey", this, e);
15974 this.onTickableFooterButtonClick(e, false, false);
15981 doRelay : function(e, fn, key){
15982 if(this.scope.isExpanded()){
15983 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15992 this.queryDelay = Math.max(this.queryDelay || 10,
15993 this.mode == 'local' ? 10 : 250);
15996 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15998 if(this.typeAhead){
15999 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16002 if(this.editable !== false){
16003 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16006 this.indicator = this.indicatorEl();
16008 if(this.indicator){
16009 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16010 this.indicator.hide();
16015 onDestroy : function(){
16017 this.view.setStore(null);
16018 this.view.el.removeAllListeners();
16019 this.view.el.remove();
16020 this.view.purgeListeners();
16023 this.list.dom.innerHTML = '';
16027 this.store.un('beforeload', this.onBeforeLoad, this);
16028 this.store.un('load', this.onLoad, this);
16029 this.store.un('loadexception', this.onLoadException, this);
16031 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16035 fireKey : function(e){
16036 if(e.isNavKeyPress() && !this.list.isVisible()){
16037 this.fireEvent("specialkey", this, e);
16042 onResize: function(w, h)
16046 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16048 // if(typeof w != 'number'){
16049 // // we do not handle it!?!?
16052 // var tw = this.trigger.getWidth();
16053 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16054 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16056 // this.inputEl().setWidth( this.adjustWidth('input', x));
16058 // //this.trigger.setStyle('left', x+'px');
16060 // if(this.list && this.listWidth === undefined){
16061 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16062 // this.list.setWidth(lw);
16063 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16071 * Allow or prevent the user from directly editing the field text. If false is passed,
16072 * the user will only be able to select from the items defined in the dropdown list. This method
16073 * is the runtime equivalent of setting the 'editable' config option at config time.
16074 * @param {Boolean} value True to allow the user to directly edit the field text
16076 setEditable : function(value){
16077 if(value == this.editable){
16080 this.editable = value;
16082 this.inputEl().dom.setAttribute('readOnly', true);
16083 this.inputEl().on('mousedown', this.onTriggerClick, this);
16084 this.inputEl().addClass('x-combo-noedit');
16086 this.inputEl().dom.setAttribute('readOnly', false);
16087 this.inputEl().un('mousedown', this.onTriggerClick, this);
16088 this.inputEl().removeClass('x-combo-noedit');
16094 onBeforeLoad : function(combo,opts){
16095 if(!this.hasFocus){
16099 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16101 this.restrictHeight();
16102 this.selectedIndex = -1;
16106 onLoad : function(){
16108 this.hasQuery = false;
16110 if(!this.hasFocus){
16114 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16115 this.loading.hide();
16118 if(this.store.getCount() > 0){
16121 this.restrictHeight();
16122 if(this.lastQuery == this.allQuery){
16123 if(this.editable && !this.tickable){
16124 this.inputEl().dom.select();
16128 !this.selectByValue(this.value, true) &&
16131 !this.store.lastOptions ||
16132 typeof(this.store.lastOptions.add) == 'undefined' ||
16133 this.store.lastOptions.add != true
16136 this.select(0, true);
16139 if(this.autoFocus){
16142 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16143 this.taTask.delay(this.typeAheadDelay);
16147 this.onEmptyResults();
16153 onLoadException : function()
16155 this.hasQuery = false;
16157 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16158 this.loading.hide();
16161 if(this.tickable && this.editable){
16166 // only causes errors at present
16167 //Roo.log(this.store.reader.jsonData);
16168 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16170 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16176 onTypeAhead : function(){
16177 if(this.store.getCount() > 0){
16178 var r = this.store.getAt(0);
16179 var newValue = r.data[this.displayField];
16180 var len = newValue.length;
16181 var selStart = this.getRawValue().length;
16183 if(selStart != len){
16184 this.setRawValue(newValue);
16185 this.selectText(selStart, newValue.length);
16191 onSelect : function(record, index){
16193 if(this.fireEvent('beforeselect', this, record, index) !== false){
16195 this.setFromData(index > -1 ? record.data : false);
16198 this.fireEvent('select', this, record, index);
16203 * Returns the currently selected field value or empty string if no value is set.
16204 * @return {String} value The selected value
16206 getValue : function()
16208 if(Roo.isIOS && this.useNativeIOS){
16209 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16213 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16216 if(this.valueField){
16217 return typeof this.value != 'undefined' ? this.value : '';
16219 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16223 getRawValue : function()
16225 if(Roo.isIOS && this.useNativeIOS){
16226 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16229 var v = this.inputEl().getValue();
16235 * Clears any text/value currently set in the field
16237 clearValue : function(){
16239 if(this.hiddenField){
16240 this.hiddenField.dom.value = '';
16243 this.setRawValue('');
16244 this.lastSelectionText = '';
16245 this.lastData = false;
16247 var close = this.closeTriggerEl();
16258 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16259 * will be displayed in the field. If the value does not match the data value of an existing item,
16260 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16261 * Otherwise the field will be blank (although the value will still be set).
16262 * @param {String} value The value to match
16264 setValue : function(v)
16266 if(Roo.isIOS && this.useNativeIOS){
16267 this.setIOSValue(v);
16277 if(this.valueField){
16278 var r = this.findRecord(this.valueField, v);
16280 text = r.data[this.displayField];
16281 }else if(this.valueNotFoundText !== undefined){
16282 text = this.valueNotFoundText;
16285 this.lastSelectionText = text;
16286 if(this.hiddenField){
16287 this.hiddenField.dom.value = v;
16289 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16292 var close = this.closeTriggerEl();
16295 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16301 * @property {Object} the last set data for the element
16306 * Sets the value of the field based on a object which is related to the record format for the store.
16307 * @param {Object} value the value to set as. or false on reset?
16309 setFromData : function(o){
16316 var dv = ''; // display value
16317 var vv = ''; // value value..
16319 if (this.displayField) {
16320 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16322 // this is an error condition!!!
16323 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16326 if(this.valueField){
16327 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16330 var close = this.closeTriggerEl();
16333 if(dv.length || vv * 1 > 0){
16335 this.blockFocus=true;
16341 if(this.hiddenField){
16342 this.hiddenField.dom.value = vv;
16344 this.lastSelectionText = dv;
16345 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16349 // no hidden field.. - we store the value in 'value', but still display
16350 // display field!!!!
16351 this.lastSelectionText = dv;
16352 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16359 reset : function(){
16360 // overridden so that last data is reset..
16367 this.setValue(this.originalValue);
16368 //this.clearInvalid();
16369 this.lastData = false;
16371 this.view.clearSelections();
16377 findRecord : function(prop, value){
16379 if(this.store.getCount() > 0){
16380 this.store.each(function(r){
16381 if(r.data[prop] == value){
16391 getName: function()
16393 // returns hidden if it's set..
16394 if (!this.rendered) {return ''};
16395 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16399 onViewMove : function(e, t){
16400 this.inKeyMode = false;
16404 onViewOver : function(e, t){
16405 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16408 var item = this.view.findItemFromChild(t);
16411 var index = this.view.indexOf(item);
16412 this.select(index, false);
16417 onViewClick : function(view, doFocus, el, e)
16419 var index = this.view.getSelectedIndexes()[0];
16421 var r = this.store.getAt(index);
16425 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16432 Roo.each(this.tickItems, function(v,k){
16434 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16436 _this.tickItems.splice(k, 1);
16438 if(typeof(e) == 'undefined' && view == false){
16439 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16451 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16452 this.tickItems.push(r.data);
16455 if(typeof(e) == 'undefined' && view == false){
16456 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16463 this.onSelect(r, index);
16465 if(doFocus !== false && !this.blockFocus){
16466 this.inputEl().focus();
16471 restrictHeight : function(){
16472 //this.innerList.dom.style.height = '';
16473 //var inner = this.innerList.dom;
16474 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16475 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16476 //this.list.beginUpdate();
16477 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16478 this.list.alignTo(this.inputEl(), this.listAlign);
16479 this.list.alignTo(this.inputEl(), this.listAlign);
16480 //this.list.endUpdate();
16484 onEmptyResults : function(){
16486 if(this.tickable && this.editable){
16487 this.hasFocus = false;
16488 this.restrictHeight();
16496 * Returns true if the dropdown list is expanded, else false.
16498 isExpanded : function(){
16499 return this.list.isVisible();
16503 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16504 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16505 * @param {String} value The data value of the item to select
16506 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16507 * selected item if it is not currently in view (defaults to true)
16508 * @return {Boolean} True if the value matched an item in the list, else false
16510 selectByValue : function(v, scrollIntoView){
16511 if(v !== undefined && v !== null){
16512 var r = this.findRecord(this.valueField || this.displayField, v);
16514 this.select(this.store.indexOf(r), scrollIntoView);
16522 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16523 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16524 * @param {Number} index The zero-based index of the list item to select
16525 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16526 * selected item if it is not currently in view (defaults to true)
16528 select : function(index, scrollIntoView){
16529 this.selectedIndex = index;
16530 this.view.select(index);
16531 if(scrollIntoView !== false){
16532 var el = this.view.getNode(index);
16534 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16537 this.list.scrollChildIntoView(el, false);
16543 selectNext : function(){
16544 var ct = this.store.getCount();
16546 if(this.selectedIndex == -1){
16548 }else if(this.selectedIndex < ct-1){
16549 this.select(this.selectedIndex+1);
16555 selectPrev : function(){
16556 var ct = this.store.getCount();
16558 if(this.selectedIndex == -1){
16560 }else if(this.selectedIndex != 0){
16561 this.select(this.selectedIndex-1);
16567 onKeyUp : function(e){
16568 if(this.editable !== false && !e.isSpecialKey()){
16569 this.lastKey = e.getKey();
16570 this.dqTask.delay(this.queryDelay);
16575 validateBlur : function(){
16576 return !this.list || !this.list.isVisible();
16580 initQuery : function(){
16582 var v = this.getRawValue();
16584 if(this.tickable && this.editable){
16585 v = this.tickableInputEl().getValue();
16592 doForce : function(){
16593 if(this.inputEl().dom.value.length > 0){
16594 this.inputEl().dom.value =
16595 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16601 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16602 * query allowing the query action to be canceled if needed.
16603 * @param {String} query The SQL query to execute
16604 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16605 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16606 * saved in the current store (defaults to false)
16608 doQuery : function(q, forceAll){
16610 if(q === undefined || q === null){
16615 forceAll: forceAll,
16619 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16624 forceAll = qe.forceAll;
16625 if(forceAll === true || (q.length >= this.minChars)){
16627 this.hasQuery = true;
16629 if(this.lastQuery != q || this.alwaysQuery){
16630 this.lastQuery = q;
16631 if(this.mode == 'local'){
16632 this.selectedIndex = -1;
16634 this.store.clearFilter();
16637 if(this.specialFilter){
16638 this.fireEvent('specialfilter', this);
16643 this.store.filter(this.displayField, q);
16646 this.store.fireEvent("datachanged", this.store);
16653 this.store.baseParams[this.queryParam] = q;
16655 var options = {params : this.getParams(q)};
16658 options.add = true;
16659 options.params.start = this.page * this.pageSize;
16662 this.store.load(options);
16665 * this code will make the page width larger, at the beginning, the list not align correctly,
16666 * we should expand the list on onLoad
16667 * so command out it
16672 this.selectedIndex = -1;
16677 this.loadNext = false;
16681 getParams : function(q){
16683 //p[this.queryParam] = q;
16687 p.limit = this.pageSize;
16693 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16695 collapse : function(){
16696 if(!this.isExpanded()){
16702 this.hasFocus = false;
16706 this.cancelBtn.hide();
16707 this.trigger.show();
16710 this.tickableInputEl().dom.value = '';
16711 this.tickableInputEl().blur();
16716 Roo.get(document).un('mousedown', this.collapseIf, this);
16717 Roo.get(document).un('mousewheel', this.collapseIf, this);
16718 if (!this.editable) {
16719 Roo.get(document).un('keydown', this.listKeyPress, this);
16721 this.fireEvent('collapse', this);
16727 collapseIf : function(e){
16728 var in_combo = e.within(this.el);
16729 var in_list = e.within(this.list);
16730 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16732 if (in_combo || in_list || is_list) {
16733 //e.stopPropagation();
16738 this.onTickableFooterButtonClick(e, false, false);
16746 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16748 expand : function(){
16750 if(this.isExpanded() || !this.hasFocus){
16754 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16755 this.list.setWidth(lw);
16761 this.restrictHeight();
16765 this.tickItems = Roo.apply([], this.item);
16768 this.cancelBtn.show();
16769 this.trigger.hide();
16772 this.tickableInputEl().focus();
16777 Roo.get(document).on('mousedown', this.collapseIf, this);
16778 Roo.get(document).on('mousewheel', this.collapseIf, this);
16779 if (!this.editable) {
16780 Roo.get(document).on('keydown', this.listKeyPress, this);
16783 this.fireEvent('expand', this);
16787 // Implements the default empty TriggerField.onTriggerClick function
16788 onTriggerClick : function(e)
16790 Roo.log('trigger click');
16792 if(this.disabled || !this.triggerList){
16797 this.loadNext = false;
16799 if(this.isExpanded()){
16801 if (!this.blockFocus) {
16802 this.inputEl().focus();
16806 this.hasFocus = true;
16807 if(this.triggerAction == 'all') {
16808 this.doQuery(this.allQuery, true);
16810 this.doQuery(this.getRawValue());
16812 if (!this.blockFocus) {
16813 this.inputEl().focus();
16818 onTickableTriggerClick : function(e)
16825 this.loadNext = false;
16826 this.hasFocus = true;
16828 if(this.triggerAction == 'all') {
16829 this.doQuery(this.allQuery, true);
16831 this.doQuery(this.getRawValue());
16835 onSearchFieldClick : function(e)
16837 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16838 this.onTickableFooterButtonClick(e, false, false);
16842 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16847 this.loadNext = false;
16848 this.hasFocus = true;
16850 if(this.triggerAction == 'all') {
16851 this.doQuery(this.allQuery, true);
16853 this.doQuery(this.getRawValue());
16857 listKeyPress : function(e)
16859 //Roo.log('listkeypress');
16860 // scroll to first matching element based on key pres..
16861 if (e.isSpecialKey()) {
16864 var k = String.fromCharCode(e.getKey()).toUpperCase();
16867 var csel = this.view.getSelectedNodes();
16868 var cselitem = false;
16870 var ix = this.view.indexOf(csel[0]);
16871 cselitem = this.store.getAt(ix);
16872 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16878 this.store.each(function(v) {
16880 // start at existing selection.
16881 if (cselitem.id == v.id) {
16887 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16888 match = this.store.indexOf(v);
16894 if (match === false) {
16895 return true; // no more action?
16898 this.view.select(match);
16899 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16900 sn.scrollIntoView(sn.dom.parentNode, false);
16903 onViewScroll : function(e, t){
16905 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){
16909 this.hasQuery = true;
16911 this.loading = this.list.select('.loading', true).first();
16913 if(this.loading === null){
16914 this.list.createChild({
16916 cls: 'loading roo-select2-more-results roo-select2-active',
16917 html: 'Loading more results...'
16920 this.loading = this.list.select('.loading', true).first();
16922 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16924 this.loading.hide();
16927 this.loading.show();
16932 this.loadNext = true;
16934 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16939 addItem : function(o)
16941 var dv = ''; // display value
16943 if (this.displayField) {
16944 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16946 // this is an error condition!!!
16947 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16954 var choice = this.choices.createChild({
16956 cls: 'roo-select2-search-choice',
16965 cls: 'roo-select2-search-choice-close fa fa-times',
16970 }, this.searchField);
16972 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16974 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16982 this.inputEl().dom.value = '';
16987 onRemoveItem : function(e, _self, o)
16989 e.preventDefault();
16991 this.lastItem = Roo.apply([], this.item);
16993 var index = this.item.indexOf(o.data) * 1;
16996 Roo.log('not this item?!');
17000 this.item.splice(index, 1);
17005 this.fireEvent('remove', this, e);
17011 syncValue : function()
17013 if(!this.item.length){
17020 Roo.each(this.item, function(i){
17021 if(_this.valueField){
17022 value.push(i[_this.valueField]);
17029 this.value = value.join(',');
17031 if(this.hiddenField){
17032 this.hiddenField.dom.value = this.value;
17035 this.store.fireEvent("datachanged", this.store);
17040 clearItem : function()
17042 if(!this.multiple){
17048 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17056 if(this.tickable && !Roo.isTouch){
17057 this.view.refresh();
17061 inputEl: function ()
17063 if(Roo.isIOS && this.useNativeIOS){
17064 return this.el.select('select.roo-ios-select', true).first();
17067 if(Roo.isTouch && this.mobileTouchView){
17068 return this.el.select('input.form-control',true).first();
17072 return this.searchField;
17075 return this.el.select('input.form-control',true).first();
17078 onTickableFooterButtonClick : function(e, btn, el)
17080 e.preventDefault();
17082 this.lastItem = Roo.apply([], this.item);
17084 if(btn && btn.name == 'cancel'){
17085 this.tickItems = Roo.apply([], this.item);
17094 Roo.each(this.tickItems, function(o){
17102 validate : function()
17104 if(this.getVisibilityEl().hasClass('hidden')){
17108 var v = this.getRawValue();
17111 v = this.getValue();
17114 if(this.disabled || this.allowBlank || v.length){
17119 this.markInvalid();
17123 tickableInputEl : function()
17125 if(!this.tickable || !this.editable){
17126 return this.inputEl();
17129 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17133 getAutoCreateTouchView : function()
17138 cls: 'form-group' //input-group
17144 type : this.inputType,
17145 cls : 'form-control x-combo-noedit',
17146 autocomplete: 'new-password',
17147 placeholder : this.placeholder || '',
17152 input.name = this.name;
17156 input.cls += ' input-' + this.size;
17159 if (this.disabled) {
17160 input.disabled = true;
17164 cls : 'roo-combobox-wrap',
17171 inputblock.cls += ' input-group';
17173 inputblock.cn.unshift({
17175 cls : 'input-group-addon input-group-prepend input-group-text',
17180 if(this.removable && !this.multiple){
17181 inputblock.cls += ' roo-removable';
17183 inputblock.cn.push({
17186 cls : 'roo-combo-removable-btn close'
17190 if(this.hasFeedback && !this.allowBlank){
17192 inputblock.cls += ' has-feedback';
17194 inputblock.cn.push({
17196 cls: 'glyphicon form-control-feedback'
17203 inputblock.cls += (this.before) ? '' : ' input-group';
17205 inputblock.cn.push({
17207 cls : 'input-group-addon input-group-append input-group-text',
17213 var ibwrap = inputblock;
17218 cls: 'roo-select2-choices',
17222 cls: 'roo-select2-search-field',
17235 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17240 cls: 'form-hidden-field'
17246 if(!this.multiple && this.showToggleBtn){
17252 if (this.caret != false) {
17255 cls: 'fa fa-' + this.caret
17262 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17264 Roo.bootstrap.version == 3 ? caret : '',
17267 cls: 'combobox-clear',
17281 combobox.cls += ' roo-select2-container-multi';
17284 var align = this.labelAlign || this.parentLabelAlign();
17286 if (align ==='left' && this.fieldLabel.length) {
17291 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17292 tooltip : 'This field is required'
17296 cls : 'control-label col-form-label',
17297 html : this.fieldLabel
17301 cls : 'roo-combobox-wrap ',
17308 var labelCfg = cfg.cn[1];
17309 var contentCfg = cfg.cn[2];
17312 if(this.indicatorpos == 'right'){
17317 cls : 'control-label col-form-label',
17321 html : this.fieldLabel
17325 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17326 tooltip : 'This field is required'
17331 cls : "roo-combobox-wrap ",
17339 labelCfg = cfg.cn[0];
17340 contentCfg = cfg.cn[1];
17345 if(this.labelWidth > 12){
17346 labelCfg.style = "width: " + this.labelWidth + 'px';
17349 if(this.labelWidth < 13 && this.labelmd == 0){
17350 this.labelmd = this.labelWidth;
17353 if(this.labellg > 0){
17354 labelCfg.cls += ' col-lg-' + this.labellg;
17355 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17358 if(this.labelmd > 0){
17359 labelCfg.cls += ' col-md-' + this.labelmd;
17360 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17363 if(this.labelsm > 0){
17364 labelCfg.cls += ' col-sm-' + this.labelsm;
17365 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17368 if(this.labelxs > 0){
17369 labelCfg.cls += ' col-xs-' + this.labelxs;
17370 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17374 } else if ( this.fieldLabel.length) {
17378 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17379 tooltip : 'This field is required'
17383 cls : 'control-label',
17384 html : this.fieldLabel
17395 if(this.indicatorpos == 'right'){
17399 cls : 'control-label',
17400 html : this.fieldLabel,
17404 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17405 tooltip : 'This field is required'
17422 var settings = this;
17424 ['xs','sm','md','lg'].map(function(size){
17425 if (settings[size]) {
17426 cfg.cls += ' col-' + size + '-' + settings[size];
17433 initTouchView : function()
17435 this.renderTouchView();
17437 this.touchViewEl.on('scroll', function(){
17438 this.el.dom.scrollTop = 0;
17441 this.originalValue = this.getValue();
17443 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17445 this.inputEl().on("click", this.showTouchView, this);
17446 if (this.triggerEl) {
17447 this.triggerEl.on("click", this.showTouchView, this);
17451 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17452 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17454 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17456 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17457 this.store.on('load', this.onTouchViewLoad, this);
17458 this.store.on('loadexception', this.onTouchViewLoadException, this);
17460 if(this.hiddenName){
17462 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17464 this.hiddenField.dom.value =
17465 this.hiddenValue !== undefined ? this.hiddenValue :
17466 this.value !== undefined ? this.value : '';
17468 this.el.dom.removeAttribute('name');
17469 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17473 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17474 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17477 if(this.removable && !this.multiple){
17478 var close = this.closeTriggerEl();
17480 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17481 close.on('click', this.removeBtnClick, this, close);
17485 * fix the bug in Safari iOS8
17487 this.inputEl().on("focus", function(e){
17488 document.activeElement.blur();
17491 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17498 renderTouchView : function()
17500 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17501 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17503 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17504 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17506 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17507 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17508 this.touchViewBodyEl.setStyle('overflow', 'auto');
17510 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17511 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17513 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17514 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17518 showTouchView : function()
17524 this.touchViewHeaderEl.hide();
17526 if(this.modalTitle.length){
17527 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17528 this.touchViewHeaderEl.show();
17531 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17532 this.touchViewEl.show();
17534 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17536 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17537 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17539 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17541 if(this.modalTitle.length){
17542 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17545 this.touchViewBodyEl.setHeight(bodyHeight);
17549 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17551 this.touchViewEl.addClass(['in','show']);
17554 if(this._touchViewMask){
17555 Roo.get(document.body).addClass("x-body-masked");
17556 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17557 this._touchViewMask.setStyle('z-index', 10000);
17558 this._touchViewMask.addClass('show');
17561 this.doTouchViewQuery();
17565 hideTouchView : function()
17567 this.touchViewEl.removeClass(['in','show']);
17571 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17573 this.touchViewEl.setStyle('display', 'none');
17576 if(this._touchViewMask){
17577 this._touchViewMask.removeClass('show');
17578 Roo.get(document.body).removeClass("x-body-masked");
17582 setTouchViewValue : function()
17589 Roo.each(this.tickItems, function(o){
17594 this.hideTouchView();
17597 doTouchViewQuery : function()
17606 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17610 if(!this.alwaysQuery || this.mode == 'local'){
17611 this.onTouchViewLoad();
17618 onTouchViewBeforeLoad : function(combo,opts)
17624 onTouchViewLoad : function()
17626 if(this.store.getCount() < 1){
17627 this.onTouchViewEmptyResults();
17631 this.clearTouchView();
17633 var rawValue = this.getRawValue();
17635 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17637 this.tickItems = [];
17639 this.store.data.each(function(d, rowIndex){
17640 var row = this.touchViewListGroup.createChild(template);
17642 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17643 row.addClass(d.data.cls);
17646 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17649 html : d.data[this.displayField]
17652 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17653 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17656 row.removeClass('selected');
17657 if(!this.multiple && this.valueField &&
17658 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17661 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17662 row.addClass('selected');
17665 if(this.multiple && this.valueField &&
17666 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17670 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671 this.tickItems.push(d.data);
17674 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17678 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17680 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17682 if(this.modalTitle.length){
17683 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17686 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17688 if(this.mobile_restrict_height && listHeight < bodyHeight){
17689 this.touchViewBodyEl.setHeight(listHeight);
17694 if(firstChecked && listHeight > bodyHeight){
17695 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17700 onTouchViewLoadException : function()
17702 this.hideTouchView();
17705 onTouchViewEmptyResults : function()
17707 this.clearTouchView();
17709 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17711 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17715 clearTouchView : function()
17717 this.touchViewListGroup.dom.innerHTML = '';
17720 onTouchViewClick : function(e, el, o)
17722 e.preventDefault();
17725 var rowIndex = o.rowIndex;
17727 var r = this.store.getAt(rowIndex);
17729 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17731 if(!this.multiple){
17732 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17733 c.dom.removeAttribute('checked');
17736 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17738 this.setFromData(r.data);
17740 var close = this.closeTriggerEl();
17746 this.hideTouchView();
17748 this.fireEvent('select', this, r, rowIndex);
17753 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17754 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17755 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17759 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17760 this.addItem(r.data);
17761 this.tickItems.push(r.data);
17765 getAutoCreateNativeIOS : function()
17768 cls: 'form-group' //input-group,
17773 cls : 'roo-ios-select'
17777 combobox.name = this.name;
17780 if (this.disabled) {
17781 combobox.disabled = true;
17784 var settings = this;
17786 ['xs','sm','md','lg'].map(function(size){
17787 if (settings[size]) {
17788 cfg.cls += ' col-' + size + '-' + settings[size];
17798 initIOSView : function()
17800 this.store.on('load', this.onIOSViewLoad, this);
17805 onIOSViewLoad : function()
17807 if(this.store.getCount() < 1){
17811 this.clearIOSView();
17813 if(this.allowBlank) {
17815 var default_text = '-- SELECT --';
17817 if(this.placeholder.length){
17818 default_text = this.placeholder;
17821 if(this.emptyTitle.length){
17822 default_text += ' - ' + this.emptyTitle + ' -';
17825 var opt = this.inputEl().createChild({
17828 html : default_text
17832 o[this.valueField] = 0;
17833 o[this.displayField] = default_text;
17835 this.ios_options.push({
17842 this.store.data.each(function(d, rowIndex){
17846 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17847 html = d.data[this.displayField];
17852 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17853 value = d.data[this.valueField];
17862 if(this.value == d.data[this.valueField]){
17863 option['selected'] = true;
17866 var opt = this.inputEl().createChild(option);
17868 this.ios_options.push({
17875 this.inputEl().on('change', function(){
17876 this.fireEvent('select', this);
17881 clearIOSView: function()
17883 this.inputEl().dom.innerHTML = '';
17885 this.ios_options = [];
17888 setIOSValue: function(v)
17892 if(!this.ios_options){
17896 Roo.each(this.ios_options, function(opts){
17898 opts.el.dom.removeAttribute('selected');
17900 if(opts.data[this.valueField] != v){
17904 opts.el.dom.setAttribute('selected', true);
17910 * @cfg {Boolean} grow
17914 * @cfg {Number} growMin
17918 * @cfg {Number} growMax
17927 Roo.apply(Roo.bootstrap.ComboBox, {
17931 cls: 'modal-header',
17953 cls: 'list-group-item',
17957 cls: 'roo-combobox-list-group-item-value'
17961 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17975 listItemCheckbox : {
17977 cls: 'list-group-item',
17981 cls: 'roo-combobox-list-group-item-value'
17985 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18001 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18006 cls: 'modal-footer',
18014 cls: 'col-xs-6 text-left',
18017 cls: 'btn btn-danger roo-touch-view-cancel',
18023 cls: 'col-xs-6 text-right',
18026 cls: 'btn btn-success roo-touch-view-ok',
18037 Roo.apply(Roo.bootstrap.ComboBox, {
18039 touchViewTemplate : {
18041 cls: 'modal fade roo-combobox-touch-view',
18045 cls: 'modal-dialog',
18046 style : 'position:fixed', // we have to fix position....
18050 cls: 'modal-content',
18052 Roo.bootstrap.ComboBox.header,
18053 Roo.bootstrap.ComboBox.body,
18054 Roo.bootstrap.ComboBox.footer
18063 * Ext JS Library 1.1.1
18064 * Copyright(c) 2006-2007, Ext JS, LLC.
18066 * Originally Released Under LGPL - original licence link has changed is not relivant.
18069 * <script type="text/javascript">
18074 * @extends Roo.util.Observable
18075 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18076 * This class also supports single and multi selection modes. <br>
18077 * Create a data model bound view:
18079 var store = new Roo.data.Store(...);
18081 var view = new Roo.View({
18083 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18085 singleSelect: true,
18086 selectedClass: "ydataview-selected",
18090 // listen for node click?
18091 view.on("click", function(vw, index, node, e){
18092 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18096 dataModel.load("foobar.xml");
18098 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18100 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18101 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18103 * Note: old style constructor is still suported (container, template, config)
18106 * Create a new View
18107 * @param {Object} config The config object
18110 Roo.View = function(config, depreciated_tpl, depreciated_config){
18112 this.parent = false;
18114 if (typeof(depreciated_tpl) == 'undefined') {
18115 // new way.. - universal constructor.
18116 Roo.apply(this, config);
18117 this.el = Roo.get(this.el);
18120 this.el = Roo.get(config);
18121 this.tpl = depreciated_tpl;
18122 Roo.apply(this, depreciated_config);
18124 this.wrapEl = this.el.wrap().wrap();
18125 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18128 if(typeof(this.tpl) == "string"){
18129 this.tpl = new Roo.Template(this.tpl);
18131 // support xtype ctors..
18132 this.tpl = new Roo.factory(this.tpl, Roo);
18136 this.tpl.compile();
18141 * @event beforeclick
18142 * Fires before a click is processed. Returns false to cancel the default action.
18143 * @param {Roo.View} this
18144 * @param {Number} index The index of the target node
18145 * @param {HTMLElement} node The target node
18146 * @param {Roo.EventObject} e The raw event object
18148 "beforeclick" : true,
18151 * Fires when a template node is clicked.
18152 * @param {Roo.View} this
18153 * @param {Number} index The index of the target node
18154 * @param {HTMLElement} node The target node
18155 * @param {Roo.EventObject} e The raw event object
18160 * Fires when a template node is double clicked.
18161 * @param {Roo.View} this
18162 * @param {Number} index The index of the target node
18163 * @param {HTMLElement} node The target node
18164 * @param {Roo.EventObject} e The raw event object
18168 * @event contextmenu
18169 * Fires when a template node is right clicked.
18170 * @param {Roo.View} this
18171 * @param {Number} index The index of the target node
18172 * @param {HTMLElement} node The target node
18173 * @param {Roo.EventObject} e The raw event object
18175 "contextmenu" : true,
18177 * @event selectionchange
18178 * Fires when the selected nodes change.
18179 * @param {Roo.View} this
18180 * @param {Array} selections Array of the selected nodes
18182 "selectionchange" : true,
18185 * @event beforeselect
18186 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18187 * @param {Roo.View} this
18188 * @param {HTMLElement} node The node to be selected
18189 * @param {Array} selections Array of currently selected nodes
18191 "beforeselect" : true,
18193 * @event preparedata
18194 * Fires on every row to render, to allow you to change the data.
18195 * @param {Roo.View} this
18196 * @param {Object} data to be rendered (change this)
18198 "preparedata" : true
18206 "click": this.onClick,
18207 "dblclick": this.onDblClick,
18208 "contextmenu": this.onContextMenu,
18212 this.selections = [];
18214 this.cmp = new Roo.CompositeElementLite([]);
18216 this.store = Roo.factory(this.store, Roo.data);
18217 this.setStore(this.store, true);
18220 if ( this.footer && this.footer.xtype) {
18222 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18224 this.footer.dataSource = this.store;
18225 this.footer.container = fctr;
18226 this.footer = Roo.factory(this.footer, Roo);
18227 fctr.insertFirst(this.el);
18229 // this is a bit insane - as the paging toolbar seems to detach the el..
18230 // dom.parentNode.parentNode.parentNode
18231 // they get detached?
18235 Roo.View.superclass.constructor.call(this);
18240 Roo.extend(Roo.View, Roo.util.Observable, {
18243 * @cfg {Roo.data.Store} store Data store to load data from.
18248 * @cfg {String|Roo.Element} el The container element.
18253 * @cfg {String|Roo.Template} tpl The template used by this View
18257 * @cfg {String} dataName the named area of the template to use as the data area
18258 * Works with domtemplates roo-name="name"
18262 * @cfg {String} selectedClass The css class to add to selected nodes
18264 selectedClass : "x-view-selected",
18266 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18271 * @cfg {String} text to display on mask (default Loading)
18275 * @cfg {Boolean} multiSelect Allow multiple selection
18277 multiSelect : false,
18279 * @cfg {Boolean} singleSelect Allow single selection
18281 singleSelect: false,
18284 * @cfg {Boolean} toggleSelect - selecting
18286 toggleSelect : false,
18289 * @cfg {Boolean} tickable - selecting
18294 * Returns the element this view is bound to.
18295 * @return {Roo.Element}
18297 getEl : function(){
18298 return this.wrapEl;
18304 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18306 refresh : function(){
18307 //Roo.log('refresh');
18310 // if we are using something like 'domtemplate', then
18311 // the what gets used is:
18312 // t.applySubtemplate(NAME, data, wrapping data..)
18313 // the outer template then get' applied with
18314 // the store 'extra data'
18315 // and the body get's added to the
18316 // roo-name="data" node?
18317 // <span class='roo-tpl-{name}'></span> ?????
18321 this.clearSelections();
18322 this.el.update("");
18324 var records = this.store.getRange();
18325 if(records.length < 1) {
18327 // is this valid?? = should it render a template??
18329 this.el.update(this.emptyText);
18333 if (this.dataName) {
18334 this.el.update(t.apply(this.store.meta)); //????
18335 el = this.el.child('.roo-tpl-' + this.dataName);
18338 for(var i = 0, len = records.length; i < len; i++){
18339 var data = this.prepareData(records[i].data, i, records[i]);
18340 this.fireEvent("preparedata", this, data, i, records[i]);
18342 var d = Roo.apply({}, data);
18345 Roo.apply(d, {'roo-id' : Roo.id()});
18349 Roo.each(this.parent.item, function(item){
18350 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18353 Roo.apply(d, {'roo-data-checked' : 'checked'});
18357 html[html.length] = Roo.util.Format.trim(
18359 t.applySubtemplate(this.dataName, d, this.store.meta) :
18366 el.update(html.join(""));
18367 this.nodes = el.dom.childNodes;
18368 this.updateIndexes(0);
18373 * Function to override to reformat the data that is sent to
18374 * the template for each node.
18375 * DEPRICATED - use the preparedata event handler.
18376 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18377 * a JSON object for an UpdateManager bound view).
18379 prepareData : function(data, index, record)
18381 this.fireEvent("preparedata", this, data, index, record);
18385 onUpdate : function(ds, record){
18386 // Roo.log('on update');
18387 this.clearSelections();
18388 var index = this.store.indexOf(record);
18389 var n = this.nodes[index];
18390 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18391 n.parentNode.removeChild(n);
18392 this.updateIndexes(index, index);
18398 onAdd : function(ds, records, index)
18400 //Roo.log(['on Add', ds, records, index] );
18401 this.clearSelections();
18402 if(this.nodes.length == 0){
18406 var n = this.nodes[index];
18407 for(var i = 0, len = records.length; i < len; i++){
18408 var d = this.prepareData(records[i].data, i, records[i]);
18410 this.tpl.insertBefore(n, d);
18413 this.tpl.append(this.el, d);
18416 this.updateIndexes(index);
18419 onRemove : function(ds, record, index){
18420 // Roo.log('onRemove');
18421 this.clearSelections();
18422 var el = this.dataName ?
18423 this.el.child('.roo-tpl-' + this.dataName) :
18426 el.dom.removeChild(this.nodes[index]);
18427 this.updateIndexes(index);
18431 * Refresh an individual node.
18432 * @param {Number} index
18434 refreshNode : function(index){
18435 this.onUpdate(this.store, this.store.getAt(index));
18438 updateIndexes : function(startIndex, endIndex){
18439 var ns = this.nodes;
18440 startIndex = startIndex || 0;
18441 endIndex = endIndex || ns.length - 1;
18442 for(var i = startIndex; i <= endIndex; i++){
18443 ns[i].nodeIndex = i;
18448 * Changes the data store this view uses and refresh the view.
18449 * @param {Store} store
18451 setStore : function(store, initial){
18452 if(!initial && this.store){
18453 this.store.un("datachanged", this.refresh);
18454 this.store.un("add", this.onAdd);
18455 this.store.un("remove", this.onRemove);
18456 this.store.un("update", this.onUpdate);
18457 this.store.un("clear", this.refresh);
18458 this.store.un("beforeload", this.onBeforeLoad);
18459 this.store.un("load", this.onLoad);
18460 this.store.un("loadexception", this.onLoad);
18464 store.on("datachanged", this.refresh, this);
18465 store.on("add", this.onAdd, this);
18466 store.on("remove", this.onRemove, this);
18467 store.on("update", this.onUpdate, this);
18468 store.on("clear", this.refresh, this);
18469 store.on("beforeload", this.onBeforeLoad, this);
18470 store.on("load", this.onLoad, this);
18471 store.on("loadexception", this.onLoad, this);
18479 * onbeforeLoad - masks the loading area.
18482 onBeforeLoad : function(store,opts)
18484 //Roo.log('onBeforeLoad');
18486 this.el.update("");
18488 this.el.mask(this.mask ? this.mask : "Loading" );
18490 onLoad : function ()
18497 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18498 * @param {HTMLElement} node
18499 * @return {HTMLElement} The template node
18501 findItemFromChild : function(node){
18502 var el = this.dataName ?
18503 this.el.child('.roo-tpl-' + this.dataName,true) :
18506 if(!node || node.parentNode == el){
18509 var p = node.parentNode;
18510 while(p && p != el){
18511 if(p.parentNode == el){
18520 onClick : function(e){
18521 var item = this.findItemFromChild(e.getTarget());
18523 var index = this.indexOf(item);
18524 if(this.onItemClick(item, index, e) !== false){
18525 this.fireEvent("click", this, index, item, e);
18528 this.clearSelections();
18533 onContextMenu : function(e){
18534 var item = this.findItemFromChild(e.getTarget());
18536 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18541 onDblClick : function(e){
18542 var item = this.findItemFromChild(e.getTarget());
18544 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18548 onItemClick : function(item, index, e)
18550 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18553 if (this.toggleSelect) {
18554 var m = this.isSelected(item) ? 'unselect' : 'select';
18557 _t[m](item, true, false);
18560 if(this.multiSelect || this.singleSelect){
18561 if(this.multiSelect && e.shiftKey && this.lastSelection){
18562 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18564 this.select(item, this.multiSelect && e.ctrlKey);
18565 this.lastSelection = item;
18568 if(!this.tickable){
18569 e.preventDefault();
18577 * Get the number of selected nodes.
18580 getSelectionCount : function(){
18581 return this.selections.length;
18585 * Get the currently selected nodes.
18586 * @return {Array} An array of HTMLElements
18588 getSelectedNodes : function(){
18589 return this.selections;
18593 * Get the indexes of the selected nodes.
18596 getSelectedIndexes : function(){
18597 var indexes = [], s = this.selections;
18598 for(var i = 0, len = s.length; i < len; i++){
18599 indexes.push(s[i].nodeIndex);
18605 * Clear all selections
18606 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18608 clearSelections : function(suppressEvent){
18609 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18610 this.cmp.elements = this.selections;
18611 this.cmp.removeClass(this.selectedClass);
18612 this.selections = [];
18613 if(!suppressEvent){
18614 this.fireEvent("selectionchange", this, this.selections);
18620 * Returns true if the passed node is selected
18621 * @param {HTMLElement/Number} node The node or node index
18622 * @return {Boolean}
18624 isSelected : function(node){
18625 var s = this.selections;
18629 node = this.getNode(node);
18630 return s.indexOf(node) !== -1;
18635 * @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
18636 * @param {Boolean} keepExisting (optional) true to keep existing selections
18637 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18639 select : function(nodeInfo, keepExisting, suppressEvent){
18640 if(nodeInfo instanceof Array){
18642 this.clearSelections(true);
18644 for(var i = 0, len = nodeInfo.length; i < len; i++){
18645 this.select(nodeInfo[i], true, true);
18649 var node = this.getNode(nodeInfo);
18650 if(!node || this.isSelected(node)){
18651 return; // already selected.
18654 this.clearSelections(true);
18657 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18658 Roo.fly(node).addClass(this.selectedClass);
18659 this.selections.push(node);
18660 if(!suppressEvent){
18661 this.fireEvent("selectionchange", this, this.selections);
18669 * @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
18670 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18671 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18673 unselect : function(nodeInfo, keepExisting, suppressEvent)
18675 if(nodeInfo instanceof Array){
18676 Roo.each(this.selections, function(s) {
18677 this.unselect(s, nodeInfo);
18681 var node = this.getNode(nodeInfo);
18682 if(!node || !this.isSelected(node)){
18683 //Roo.log("not selected");
18684 return; // not selected.
18688 Roo.each(this.selections, function(s) {
18690 Roo.fly(node).removeClass(this.selectedClass);
18697 this.selections= ns;
18698 this.fireEvent("selectionchange", this, this.selections);
18702 * Gets a template node.
18703 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18704 * @return {HTMLElement} The node or null if it wasn't found
18706 getNode : function(nodeInfo){
18707 if(typeof nodeInfo == "string"){
18708 return document.getElementById(nodeInfo);
18709 }else if(typeof nodeInfo == "number"){
18710 return this.nodes[nodeInfo];
18716 * Gets a range template nodes.
18717 * @param {Number} startIndex
18718 * @param {Number} endIndex
18719 * @return {Array} An array of nodes
18721 getNodes : function(start, end){
18722 var ns = this.nodes;
18723 start = start || 0;
18724 end = typeof end == "undefined" ? ns.length - 1 : end;
18727 for(var i = start; i <= end; i++){
18731 for(var i = start; i >= end; i--){
18739 * Finds the index of the passed node
18740 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18741 * @return {Number} The index of the node or -1
18743 indexOf : function(node){
18744 node = this.getNode(node);
18745 if(typeof node.nodeIndex == "number"){
18746 return node.nodeIndex;
18748 var ns = this.nodes;
18749 for(var i = 0, len = ns.length; i < len; i++){
18760 * based on jquery fullcalendar
18764 Roo.bootstrap = Roo.bootstrap || {};
18766 * @class Roo.bootstrap.Calendar
18767 * @extends Roo.bootstrap.Component
18768 * Bootstrap Calendar class
18769 * @cfg {Boolean} loadMask (true|false) default false
18770 * @cfg {Object} header generate the user specific header of the calendar, default false
18773 * Create a new Container
18774 * @param {Object} config The config object
18779 Roo.bootstrap.Calendar = function(config){
18780 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18784 * Fires when a date is selected
18785 * @param {DatePicker} this
18786 * @param {Date} date The selected date
18790 * @event monthchange
18791 * Fires when the displayed month changes
18792 * @param {DatePicker} this
18793 * @param {Date} date The selected month
18795 'monthchange': true,
18797 * @event evententer
18798 * Fires when mouse over an event
18799 * @param {Calendar} this
18800 * @param {event} Event
18802 'evententer': true,
18804 * @event eventleave
18805 * Fires when the mouse leaves an
18806 * @param {Calendar} this
18809 'eventleave': true,
18811 * @event eventclick
18812 * Fires when the mouse click an
18813 * @param {Calendar} this
18822 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18825 * @cfg {Number} startDay
18826 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18834 getAutoCreate : function(){
18837 var fc_button = function(name, corner, style, content ) {
18838 return Roo.apply({},{
18840 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18842 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18845 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18856 style : 'width:100%',
18863 cls : 'fc-header-left',
18865 fc_button('prev', 'left', 'arrow', '‹' ),
18866 fc_button('next', 'right', 'arrow', '›' ),
18867 { tag: 'span', cls: 'fc-header-space' },
18868 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18876 cls : 'fc-header-center',
18880 cls: 'fc-header-title',
18883 html : 'month / year'
18891 cls : 'fc-header-right',
18893 /* fc_button('month', 'left', '', 'month' ),
18894 fc_button('week', '', '', 'week' ),
18895 fc_button('day', 'right', '', 'day' )
18907 header = this.header;
18910 var cal_heads = function() {
18912 // fixme - handle this.
18914 for (var i =0; i < Date.dayNames.length; i++) {
18915 var d = Date.dayNames[i];
18918 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18919 html : d.substring(0,3)
18923 ret[0].cls += ' fc-first';
18924 ret[6].cls += ' fc-last';
18927 var cal_cell = function(n) {
18930 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18935 cls: 'fc-day-number',
18939 cls: 'fc-day-content',
18943 style: 'position: relative;' // height: 17px;
18955 var cal_rows = function() {
18958 for (var r = 0; r < 6; r++) {
18965 for (var i =0; i < Date.dayNames.length; i++) {
18966 var d = Date.dayNames[i];
18967 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18970 row.cn[0].cls+=' fc-first';
18971 row.cn[0].cn[0].style = 'min-height:90px';
18972 row.cn[6].cls+=' fc-last';
18976 ret[0].cls += ' fc-first';
18977 ret[4].cls += ' fc-prev-last';
18978 ret[5].cls += ' fc-last';
18985 cls: 'fc-border-separate',
18986 style : 'width:100%',
18994 cls : 'fc-first fc-last',
19012 cls : 'fc-content',
19013 style : "position: relative;",
19016 cls : 'fc-view fc-view-month fc-grid',
19017 style : 'position: relative',
19018 unselectable : 'on',
19021 cls : 'fc-event-container',
19022 style : 'position:absolute;z-index:8;top:0;left:0;'
19040 initEvents : function()
19043 throw "can not find store for calendar";
19049 style: "text-align:center",
19053 style: "background-color:white;width:50%;margin:250 auto",
19057 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19068 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19070 var size = this.el.select('.fc-content', true).first().getSize();
19071 this.maskEl.setSize(size.width, size.height);
19072 this.maskEl.enableDisplayMode("block");
19073 if(!this.loadMask){
19074 this.maskEl.hide();
19077 this.store = Roo.factory(this.store, Roo.data);
19078 this.store.on('load', this.onLoad, this);
19079 this.store.on('beforeload', this.onBeforeLoad, this);
19083 this.cells = this.el.select('.fc-day',true);
19084 //Roo.log(this.cells);
19085 this.textNodes = this.el.query('.fc-day-number');
19086 this.cells.addClassOnOver('fc-state-hover');
19088 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19089 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19090 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19091 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19093 this.on('monthchange', this.onMonthChange, this);
19095 this.update(new Date().clearTime());
19098 resize : function() {
19099 var sz = this.el.getSize();
19101 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19102 this.el.select('.fc-day-content div',true).setHeight(34);
19107 showPrevMonth : function(e){
19108 this.update(this.activeDate.add("mo", -1));
19110 showToday : function(e){
19111 this.update(new Date().clearTime());
19114 showNextMonth : function(e){
19115 this.update(this.activeDate.add("mo", 1));
19119 showPrevYear : function(){
19120 this.update(this.activeDate.add("y", -1));
19124 showNextYear : function(){
19125 this.update(this.activeDate.add("y", 1));
19130 update : function(date)
19132 var vd = this.activeDate;
19133 this.activeDate = date;
19134 // if(vd && this.el){
19135 // var t = date.getTime();
19136 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19137 // Roo.log('using add remove');
19139 // this.fireEvent('monthchange', this, date);
19141 // this.cells.removeClass("fc-state-highlight");
19142 // this.cells.each(function(c){
19143 // if(c.dateValue == t){
19144 // c.addClass("fc-state-highlight");
19145 // setTimeout(function(){
19146 // try{c.dom.firstChild.focus();}catch(e){}
19156 var days = date.getDaysInMonth();
19158 var firstOfMonth = date.getFirstDateOfMonth();
19159 var startingPos = firstOfMonth.getDay()-this.startDay;
19161 if(startingPos < this.startDay){
19165 var pm = date.add(Date.MONTH, -1);
19166 var prevStart = pm.getDaysInMonth()-startingPos;
19168 this.cells = this.el.select('.fc-day',true);
19169 this.textNodes = this.el.query('.fc-day-number');
19170 this.cells.addClassOnOver('fc-state-hover');
19172 var cells = this.cells.elements;
19173 var textEls = this.textNodes;
19175 Roo.each(cells, function(cell){
19176 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19179 days += startingPos;
19181 // convert everything to numbers so it's fast
19182 var day = 86400000;
19183 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19186 //Roo.log(prevStart);
19188 var today = new Date().clearTime().getTime();
19189 var sel = date.clearTime().getTime();
19190 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19191 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19192 var ddMatch = this.disabledDatesRE;
19193 var ddText = this.disabledDatesText;
19194 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19195 var ddaysText = this.disabledDaysText;
19196 var format = this.format;
19198 var setCellClass = function(cal, cell){
19202 //Roo.log('set Cell Class');
19204 var t = d.getTime();
19208 cell.dateValue = t;
19210 cell.className += " fc-today";
19211 cell.className += " fc-state-highlight";
19212 cell.title = cal.todayText;
19215 // disable highlight in other month..
19216 //cell.className += " fc-state-highlight";
19221 cell.className = " fc-state-disabled";
19222 cell.title = cal.minText;
19226 cell.className = " fc-state-disabled";
19227 cell.title = cal.maxText;
19231 if(ddays.indexOf(d.getDay()) != -1){
19232 cell.title = ddaysText;
19233 cell.className = " fc-state-disabled";
19236 if(ddMatch && format){
19237 var fvalue = d.dateFormat(format);
19238 if(ddMatch.test(fvalue)){
19239 cell.title = ddText.replace("%0", fvalue);
19240 cell.className = " fc-state-disabled";
19244 if (!cell.initialClassName) {
19245 cell.initialClassName = cell.dom.className;
19248 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19253 for(; i < startingPos; i++) {
19254 textEls[i].innerHTML = (++prevStart);
19255 d.setDate(d.getDate()+1);
19257 cells[i].className = "fc-past fc-other-month";
19258 setCellClass(this, cells[i]);
19263 for(; i < days; i++){
19264 intDay = i - startingPos + 1;
19265 textEls[i].innerHTML = (intDay);
19266 d.setDate(d.getDate()+1);
19268 cells[i].className = ''; // "x-date-active";
19269 setCellClass(this, cells[i]);
19273 for(; i < 42; i++) {
19274 textEls[i].innerHTML = (++extraDays);
19275 d.setDate(d.getDate()+1);
19277 cells[i].className = "fc-future fc-other-month";
19278 setCellClass(this, cells[i]);
19281 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19283 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19285 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19286 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19288 if(totalRows != 6){
19289 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19290 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19293 this.fireEvent('monthchange', this, date);
19297 if(!this.internalRender){
19298 var main = this.el.dom.firstChild;
19299 var w = main.offsetWidth;
19300 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19301 Roo.fly(main).setWidth(w);
19302 this.internalRender = true;
19303 // opera does not respect the auto grow header center column
19304 // then, after it gets a width opera refuses to recalculate
19305 // without a second pass
19306 if(Roo.isOpera && !this.secondPass){
19307 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19308 this.secondPass = true;
19309 this.update.defer(10, this, [date]);
19316 findCell : function(dt) {
19317 dt = dt.clearTime().getTime();
19319 this.cells.each(function(c){
19320 //Roo.log("check " +c.dateValue + '?=' + dt);
19321 if(c.dateValue == dt){
19331 findCells : function(ev) {
19332 var s = ev.start.clone().clearTime().getTime();
19334 var e= ev.end.clone().clearTime().getTime();
19337 this.cells.each(function(c){
19338 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19340 if(c.dateValue > e){
19343 if(c.dateValue < s){
19352 // findBestRow: function(cells)
19356 // for (var i =0 ; i < cells.length;i++) {
19357 // ret = Math.max(cells[i].rows || 0,ret);
19364 addItem : function(ev)
19366 // look for vertical location slot in
19367 var cells = this.findCells(ev);
19369 // ev.row = this.findBestRow(cells);
19371 // work out the location.
19375 for(var i =0; i < cells.length; i++) {
19377 cells[i].row = cells[0].row;
19380 cells[i].row = cells[i].row + 1;
19390 if (crow.start.getY() == cells[i].getY()) {
19392 crow.end = cells[i];
19409 cells[0].events.push(ev);
19411 this.calevents.push(ev);
19414 clearEvents: function() {
19416 if(!this.calevents){
19420 Roo.each(this.cells.elements, function(c){
19426 Roo.each(this.calevents, function(e) {
19427 Roo.each(e.els, function(el) {
19428 el.un('mouseenter' ,this.onEventEnter, this);
19429 el.un('mouseleave' ,this.onEventLeave, this);
19434 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19440 renderEvents: function()
19444 this.cells.each(function(c) {
19453 if(c.row != c.events.length){
19454 r = 4 - (4 - (c.row - c.events.length));
19457 c.events = ev.slice(0, r);
19458 c.more = ev.slice(r);
19460 if(c.more.length && c.more.length == 1){
19461 c.events.push(c.more.pop());
19464 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19468 this.cells.each(function(c) {
19470 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19473 for (var e = 0; e < c.events.length; e++){
19474 var ev = c.events[e];
19475 var rows = ev.rows;
19477 for(var i = 0; i < rows.length; i++) {
19479 // how many rows should it span..
19482 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19483 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19485 unselectable : "on",
19488 cls: 'fc-event-inner',
19492 // cls: 'fc-event-time',
19493 // html : cells.length > 1 ? '' : ev.time
19497 cls: 'fc-event-title',
19498 html : String.format('{0}', ev.title)
19505 cls: 'ui-resizable-handle ui-resizable-e',
19506 html : '  '
19513 cfg.cls += ' fc-event-start';
19515 if ((i+1) == rows.length) {
19516 cfg.cls += ' fc-event-end';
19519 var ctr = _this.el.select('.fc-event-container',true).first();
19520 var cg = ctr.createChild(cfg);
19522 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19523 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19525 var r = (c.more.length) ? 1 : 0;
19526 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19527 cg.setWidth(ebox.right - sbox.x -2);
19529 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19530 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19531 cg.on('click', _this.onEventClick, _this, ev);
19542 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19543 style : 'position: absolute',
19544 unselectable : "on",
19547 cls: 'fc-event-inner',
19551 cls: 'fc-event-title',
19559 cls: 'ui-resizable-handle ui-resizable-e',
19560 html : '  '
19566 var ctr = _this.el.select('.fc-event-container',true).first();
19567 var cg = ctr.createChild(cfg);
19569 var sbox = c.select('.fc-day-content',true).first().getBox();
19570 var ebox = c.select('.fc-day-content',true).first().getBox();
19572 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19573 cg.setWidth(ebox.right - sbox.x -2);
19575 cg.on('click', _this.onMoreEventClick, _this, c.more);
19585 onEventEnter: function (e, el,event,d) {
19586 this.fireEvent('evententer', this, el, event);
19589 onEventLeave: function (e, el,event,d) {
19590 this.fireEvent('eventleave', this, el, event);
19593 onEventClick: function (e, el,event,d) {
19594 this.fireEvent('eventclick', this, el, event);
19597 onMonthChange: function () {
19601 onMoreEventClick: function(e, el, more)
19605 this.calpopover.placement = 'right';
19606 this.calpopover.setTitle('More');
19608 this.calpopover.setContent('');
19610 var ctr = this.calpopover.el.select('.popover-content', true).first();
19612 Roo.each(more, function(m){
19614 cls : 'fc-event-hori fc-event-draggable',
19617 var cg = ctr.createChild(cfg);
19619 cg.on('click', _this.onEventClick, _this, m);
19622 this.calpopover.show(el);
19627 onLoad: function ()
19629 this.calevents = [];
19632 if(this.store.getCount() > 0){
19633 this.store.data.each(function(d){
19636 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19637 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19638 time : d.data.start_time,
19639 title : d.data.title,
19640 description : d.data.description,
19641 venue : d.data.venue
19646 this.renderEvents();
19648 if(this.calevents.length && this.loadMask){
19649 this.maskEl.hide();
19653 onBeforeLoad: function()
19655 this.clearEvents();
19657 this.maskEl.show();
19671 * @class Roo.bootstrap.Popover
19672 * @extends Roo.bootstrap.Component
19673 * Bootstrap Popover class
19674 * @cfg {String} html contents of the popover (or false to use children..)
19675 * @cfg {String} title of popover (or false to hide)
19676 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19677 * @cfg {String} trigger click || hover (or false to trigger manually)
19678 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19679 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19680 * - if false and it has a 'parent' then it will be automatically added to that element
19681 * - if string - Roo.get will be called
19682 * @cfg {Number} delay - delay before showing
19685 * Create a new Popover
19686 * @param {Object} config The config object
19689 Roo.bootstrap.Popover = function(config){
19690 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19696 * After the popover show
19698 * @param {Roo.bootstrap.Popover} this
19703 * After the popover hide
19705 * @param {Roo.bootstrap.Popover} this
19711 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19716 placement : 'right',
19717 trigger : 'hover', // hover
19723 can_build_overlaid : false,
19725 maskEl : false, // the mask element
19728 alignEl : false, // when show is called with an element - this get's stored.
19730 getChildContainer : function()
19732 return this.contentEl;
19735 getPopoverHeader : function()
19737 this.title = true; // flag not to hide it..
19738 this.headerEl.addClass('p-0');
19739 return this.headerEl
19743 getAutoCreate : function(){
19746 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19747 style: 'display:block',
19753 cls : 'popover-inner ',
19757 cls: 'popover-title popover-header',
19758 html : this.title === false ? '' : this.title
19761 cls : 'popover-content popover-body ' + (this.cls || ''),
19762 html : this.html || ''
19773 * @param {string} the title
19775 setTitle: function(str)
19779 this.headerEl.dom.innerHTML = str;
19784 * @param {string} the body content
19786 setContent: function(str)
19789 if (this.contentEl) {
19790 this.contentEl.dom.innerHTML = str;
19794 // as it get's added to the bottom of the page.
19795 onRender : function(ct, position)
19797 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19802 var cfg = Roo.apply({}, this.getAutoCreate());
19806 cfg.cls += ' ' + this.cls;
19809 cfg.style = this.style;
19811 //Roo.log("adding to ");
19812 this.el = Roo.get(document.body).createChild(cfg, position);
19813 // Roo.log(this.el);
19816 this.contentEl = this.el.select('.popover-content',true).first();
19817 this.headerEl = this.el.select('.popover-title',true).first();
19820 if(typeof(this.items) != 'undefined'){
19821 var items = this.items;
19824 for(var i =0;i < items.length;i++) {
19825 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19829 this.items = nitems;
19831 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19832 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19839 resizeMask : function()
19841 this.maskEl.setSize(
19842 Roo.lib.Dom.getViewWidth(true),
19843 Roo.lib.Dom.getViewHeight(true)
19847 initEvents : function()
19851 Roo.bootstrap.Popover.register(this);
19854 this.arrowEl = this.el.select('.arrow',true).first();
19855 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19856 this.el.enableDisplayMode('block');
19860 if (this.over === false && !this.parent()) {
19863 if (this.triggers === false) {
19868 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19869 var triggers = this.trigger ? this.trigger.split(' ') : [];
19870 Roo.each(triggers, function(trigger) {
19872 if (trigger == 'click') {
19873 on_el.on('click', this.toggle, this);
19874 } else if (trigger != 'manual') {
19875 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19876 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19878 on_el.on(eventIn ,this.enter, this);
19879 on_el.on(eventOut, this.leave, this);
19889 toggle : function () {
19890 this.hoverState == 'in' ? this.leave() : this.enter();
19893 enter : function () {
19895 clearTimeout(this.timeout);
19897 this.hoverState = 'in';
19899 if (!this.delay || !this.delay.show) {
19904 this.timeout = setTimeout(function () {
19905 if (_t.hoverState == 'in') {
19908 }, this.delay.show)
19911 leave : function() {
19912 clearTimeout(this.timeout);
19914 this.hoverState = 'out';
19916 if (!this.delay || !this.delay.hide) {
19921 this.timeout = setTimeout(function () {
19922 if (_t.hoverState == 'out') {
19925 }, this.delay.hide)
19929 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19930 * @param {string} (left|right|top|bottom) position
19932 show : function (on_el, placement)
19934 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19935 on_el = on_el || false; // default to false
19938 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19939 on_el = this.parent().el;
19940 } else if (this.over) {
19941 Roo.get(this.over);
19946 this.alignEl = Roo.get( on_el );
19949 this.render(document.body);
19955 if (this.title === false) {
19956 this.headerEl.hide();
19961 this.el.dom.style.display = 'block';
19964 if (this.alignEl) {
19965 this.updatePosition(this.placement, true);
19968 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19969 var es = this.el.getSize();
19970 var x = Roo.lib.Dom.getViewWidth()/2;
19971 var y = Roo.lib.Dom.getViewHeight()/2;
19972 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19977 //var arrow = this.el.select('.arrow',true).first();
19978 //arrow.set(align[2],
19980 this.el.addClass('in');
19984 this.hoverState = 'in';
19987 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19988 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19989 this.maskEl.dom.style.display = 'block';
19990 this.maskEl.addClass('show');
19992 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19994 this.fireEvent('show', this);
19998 * fire this manually after loading a grid in the table for example
19999 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20000 * @param {Boolean} try and move it if we cant get right position.
20002 updatePosition : function(placement, try_move)
20004 // allow for calling with no parameters
20005 placement = placement ? placement : this.placement;
20006 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20008 this.el.removeClass([
20009 'fade','top','bottom', 'left', 'right','in',
20010 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20012 this.el.addClass(placement + ' bs-popover-' + placement);
20014 if (!this.alignEl ) {
20018 switch (placement) {
20020 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20021 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20022 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20023 //normal display... or moved up/down.
20024 this.el.setXY(offset);
20025 var xy = this.alignEl.getAnchorXY('tr', false);
20027 this.arrowEl.setXY(xy);
20030 // continue through...
20031 return this.updatePosition('left', false);
20035 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20036 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20037 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20038 //normal display... or moved up/down.
20039 this.el.setXY(offset);
20040 var xy = this.alignEl.getAnchorXY('tl', false);
20041 xy[0]-=10;xy[1]+=5; // << fix me
20042 this.arrowEl.setXY(xy);
20046 return this.updatePosition('right', false);
20049 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20050 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20051 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20052 //normal display... or moved up/down.
20053 this.el.setXY(offset);
20054 var xy = this.alignEl.getAnchorXY('t', false);
20055 xy[1]-=10; // << fix me
20056 this.arrowEl.setXY(xy);
20060 return this.updatePosition('bottom', false);
20063 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20064 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20065 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20066 //normal display... or moved up/down.
20067 this.el.setXY(offset);
20068 var xy = this.alignEl.getAnchorXY('b', false);
20069 xy[1]+=2; // << fix me
20070 this.arrowEl.setXY(xy);
20074 return this.updatePosition('top', false);
20085 this.el.setXY([0,0]);
20086 this.el.removeClass('in');
20088 this.hoverState = null;
20089 this.maskEl.hide(); // always..
20090 this.fireEvent('hide', this);
20096 Roo.apply(Roo.bootstrap.Popover, {
20099 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20100 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20101 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20102 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20107 clickHander : false,
20110 onMouseDown : function(e)
20112 if (!e.getTarget(".roo-popover")) {
20120 register : function(popup)
20122 if (!Roo.bootstrap.Popover.clickHandler) {
20123 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20125 // hide other popups.
20127 this.popups.push(popup);
20129 hideAll : function()
20131 this.popups.forEach(function(p) {
20139 * Card header - holder for the card header elements.
20144 * @class Roo.bootstrap.PopoverNav
20145 * @extends Roo.bootstrap.NavGroup
20146 * Bootstrap Popover header navigation class
20148 * Create a new Popover Header Navigation
20149 * @param {Object} config The config object
20152 Roo.bootstrap.PopoverNav = function(config){
20153 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20156 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20159 container_method : 'getPopoverHeader'
20177 * @class Roo.bootstrap.Progress
20178 * @extends Roo.bootstrap.Component
20179 * Bootstrap Progress class
20180 * @cfg {Boolean} striped striped of the progress bar
20181 * @cfg {Boolean} active animated of the progress bar
20185 * Create a new Progress
20186 * @param {Object} config The config object
20189 Roo.bootstrap.Progress = function(config){
20190 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20193 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20198 getAutoCreate : function(){
20206 cfg.cls += ' progress-striped';
20210 cfg.cls += ' active';
20229 * @class Roo.bootstrap.ProgressBar
20230 * @extends Roo.bootstrap.Component
20231 * Bootstrap ProgressBar class
20232 * @cfg {Number} aria_valuenow aria-value now
20233 * @cfg {Number} aria_valuemin aria-value min
20234 * @cfg {Number} aria_valuemax aria-value max
20235 * @cfg {String} label label for the progress bar
20236 * @cfg {String} panel (success | info | warning | danger )
20237 * @cfg {String} role role of the progress bar
20238 * @cfg {String} sr_only text
20242 * Create a new ProgressBar
20243 * @param {Object} config The config object
20246 Roo.bootstrap.ProgressBar = function(config){
20247 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20250 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20254 aria_valuemax : 100,
20260 getAutoCreate : function()
20265 cls: 'progress-bar',
20266 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20278 cfg.role = this.role;
20281 if(this.aria_valuenow){
20282 cfg['aria-valuenow'] = this.aria_valuenow;
20285 if(this.aria_valuemin){
20286 cfg['aria-valuemin'] = this.aria_valuemin;
20289 if(this.aria_valuemax){
20290 cfg['aria-valuemax'] = this.aria_valuemax;
20293 if(this.label && !this.sr_only){
20294 cfg.html = this.label;
20298 cfg.cls += ' progress-bar-' + this.panel;
20304 update : function(aria_valuenow)
20306 this.aria_valuenow = aria_valuenow;
20308 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20323 * @class Roo.bootstrap.TabGroup
20324 * @extends Roo.bootstrap.Column
20325 * Bootstrap Column class
20326 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20327 * @cfg {Boolean} carousel true to make the group behave like a carousel
20328 * @cfg {Boolean} bullets show bullets for the panels
20329 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20330 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20331 * @cfg {Boolean} showarrow (true|false) show arrow default true
20334 * Create a new TabGroup
20335 * @param {Object} config The config object
20338 Roo.bootstrap.TabGroup = function(config){
20339 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20341 this.navId = Roo.id();
20344 Roo.bootstrap.TabGroup.register(this);
20348 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20351 transition : false,
20356 slideOnTouch : false,
20359 getAutoCreate : function()
20361 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20363 cfg.cls += ' tab-content';
20365 if (this.carousel) {
20366 cfg.cls += ' carousel slide';
20369 cls : 'carousel-inner',
20373 if(this.bullets && !Roo.isTouch){
20376 cls : 'carousel-bullets',
20380 if(this.bullets_cls){
20381 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20388 cfg.cn[0].cn.push(bullets);
20391 if(this.showarrow){
20392 cfg.cn[0].cn.push({
20394 class : 'carousel-arrow',
20398 class : 'carousel-prev',
20402 class : 'fa fa-chevron-left'
20408 class : 'carousel-next',
20412 class : 'fa fa-chevron-right'
20425 initEvents: function()
20427 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20428 // this.el.on("touchstart", this.onTouchStart, this);
20431 if(this.autoslide){
20434 this.slideFn = window.setInterval(function() {
20435 _this.showPanelNext();
20439 if(this.showarrow){
20440 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20441 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20447 // onTouchStart : function(e, el, o)
20449 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20453 // this.showPanelNext();
20457 getChildContainer : function()
20459 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20463 * register a Navigation item
20464 * @param {Roo.bootstrap.NavItem} the navitem to add
20466 register : function(item)
20468 this.tabs.push( item);
20469 item.navId = this.navId; // not really needed..
20474 getActivePanel : function()
20477 Roo.each(this.tabs, function(t) {
20487 getPanelByName : function(n)
20490 Roo.each(this.tabs, function(t) {
20491 if (t.tabId == n) {
20499 indexOfPanel : function(p)
20502 Roo.each(this.tabs, function(t,i) {
20503 if (t.tabId == p.tabId) {
20512 * show a specific panel
20513 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20514 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20516 showPanel : function (pan)
20518 if(this.transition || typeof(pan) == 'undefined'){
20519 Roo.log("waiting for the transitionend");
20523 if (typeof(pan) == 'number') {
20524 pan = this.tabs[pan];
20527 if (typeof(pan) == 'string') {
20528 pan = this.getPanelByName(pan);
20531 var cur = this.getActivePanel();
20534 Roo.log('pan or acitve pan is undefined');
20538 if (pan.tabId == this.getActivePanel().tabId) {
20542 if (false === cur.fireEvent('beforedeactivate')) {
20546 if(this.bullets > 0 && !Roo.isTouch){
20547 this.setActiveBullet(this.indexOfPanel(pan));
20550 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20552 //class="carousel-item carousel-item-next carousel-item-left"
20554 this.transition = true;
20555 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20556 var lr = dir == 'next' ? 'left' : 'right';
20557 pan.el.addClass(dir); // or prev
20558 pan.el.addClass('carousel-item-' + dir); // or prev
20559 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20560 cur.el.addClass(lr); // or right
20561 pan.el.addClass(lr);
20562 cur.el.addClass('carousel-item-' +lr); // or right
20563 pan.el.addClass('carousel-item-' +lr);
20567 cur.el.on('transitionend', function() {
20568 Roo.log("trans end?");
20570 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20571 pan.setActive(true);
20573 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20574 cur.setActive(false);
20576 _this.transition = false;
20578 }, this, { single: true } );
20583 cur.setActive(false);
20584 pan.setActive(true);
20589 showPanelNext : function()
20591 var i = this.indexOfPanel(this.getActivePanel());
20593 if (i >= this.tabs.length - 1 && !this.autoslide) {
20597 if (i >= this.tabs.length - 1 && this.autoslide) {
20601 this.showPanel(this.tabs[i+1]);
20604 showPanelPrev : function()
20606 var i = this.indexOfPanel(this.getActivePanel());
20608 if (i < 1 && !this.autoslide) {
20612 if (i < 1 && this.autoslide) {
20613 i = this.tabs.length;
20616 this.showPanel(this.tabs[i-1]);
20620 addBullet: function()
20622 if(!this.bullets || Roo.isTouch){
20625 var ctr = this.el.select('.carousel-bullets',true).first();
20626 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20627 var bullet = ctr.createChild({
20628 cls : 'bullet bullet-' + i
20629 },ctr.dom.lastChild);
20634 bullet.on('click', (function(e, el, o, ii, t){
20636 e.preventDefault();
20638 this.showPanel(ii);
20640 if(this.autoslide && this.slideFn){
20641 clearInterval(this.slideFn);
20642 this.slideFn = window.setInterval(function() {
20643 _this.showPanelNext();
20647 }).createDelegate(this, [i, bullet], true));
20652 setActiveBullet : function(i)
20658 Roo.each(this.el.select('.bullet', true).elements, function(el){
20659 el.removeClass('selected');
20662 var bullet = this.el.select('.bullet-' + i, true).first();
20668 bullet.addClass('selected');
20679 Roo.apply(Roo.bootstrap.TabGroup, {
20683 * register a Navigation Group
20684 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20686 register : function(navgrp)
20688 this.groups[navgrp.navId] = navgrp;
20692 * fetch a Navigation Group based on the navigation ID
20693 * if one does not exist , it will get created.
20694 * @param {string} the navgroup to add
20695 * @returns {Roo.bootstrap.NavGroup} the navgroup
20697 get: function(navId) {
20698 if (typeof(this.groups[navId]) == 'undefined') {
20699 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20701 return this.groups[navId] ;
20716 * @class Roo.bootstrap.TabPanel
20717 * @extends Roo.bootstrap.Component
20718 * Bootstrap TabPanel class
20719 * @cfg {Boolean} active panel active
20720 * @cfg {String} html panel content
20721 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20722 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20723 * @cfg {String} href click to link..
20724 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20728 * Create a new TabPanel
20729 * @param {Object} config The config object
20732 Roo.bootstrap.TabPanel = function(config){
20733 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20737 * Fires when the active status changes
20738 * @param {Roo.bootstrap.TabPanel} this
20739 * @param {Boolean} state the new state
20744 * @event beforedeactivate
20745 * Fires before a tab is de-activated - can be used to do validation on a form.
20746 * @param {Roo.bootstrap.TabPanel} this
20747 * @return {Boolean} false if there is an error
20750 'beforedeactivate': true
20753 this.tabId = this.tabId || Roo.id();
20757 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20764 touchSlide : false,
20765 getAutoCreate : function(){
20770 // item is needed for carousel - not sure if it has any effect otherwise
20771 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20772 html: this.html || ''
20776 cfg.cls += ' active';
20780 cfg.tabId = this.tabId;
20788 initEvents: function()
20790 var p = this.parent();
20792 this.navId = this.navId || p.navId;
20794 if (typeof(this.navId) != 'undefined') {
20795 // not really needed.. but just in case.. parent should be a NavGroup.
20796 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20800 var i = tg.tabs.length - 1;
20802 if(this.active && tg.bullets > 0 && i < tg.bullets){
20803 tg.setActiveBullet(i);
20807 this.el.on('click', this.onClick, this);
20809 if(Roo.isTouch && this.touchSlide){
20810 this.el.on("touchstart", this.onTouchStart, this);
20811 this.el.on("touchmove", this.onTouchMove, this);
20812 this.el.on("touchend", this.onTouchEnd, this);
20817 onRender : function(ct, position)
20819 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20822 setActive : function(state)
20824 Roo.log("panel - set active " + this.tabId + "=" + state);
20826 this.active = state;
20828 this.el.removeClass('active');
20830 } else if (!this.el.hasClass('active')) {
20831 this.el.addClass('active');
20834 this.fireEvent('changed', this, state);
20837 onClick : function(e)
20839 e.preventDefault();
20841 if(!this.href.length){
20845 window.location.href = this.href;
20854 onTouchStart : function(e)
20856 this.swiping = false;
20858 this.startX = e.browserEvent.touches[0].clientX;
20859 this.startY = e.browserEvent.touches[0].clientY;
20862 onTouchMove : function(e)
20864 this.swiping = true;
20866 this.endX = e.browserEvent.touches[0].clientX;
20867 this.endY = e.browserEvent.touches[0].clientY;
20870 onTouchEnd : function(e)
20877 var tabGroup = this.parent();
20879 if(this.endX > this.startX){ // swiping right
20880 tabGroup.showPanelPrev();
20884 if(this.startX > this.endX){ // swiping left
20885 tabGroup.showPanelNext();
20904 * @class Roo.bootstrap.DateField
20905 * @extends Roo.bootstrap.Input
20906 * Bootstrap DateField class
20907 * @cfg {Number} weekStart default 0
20908 * @cfg {String} viewMode default empty, (months|years)
20909 * @cfg {String} minViewMode default empty, (months|years)
20910 * @cfg {Number} startDate default -Infinity
20911 * @cfg {Number} endDate default Infinity
20912 * @cfg {Boolean} todayHighlight default false
20913 * @cfg {Boolean} todayBtn default false
20914 * @cfg {Boolean} calendarWeeks default false
20915 * @cfg {Object} daysOfWeekDisabled default empty
20916 * @cfg {Boolean} singleMode default false (true | false)
20918 * @cfg {Boolean} keyboardNavigation default true
20919 * @cfg {String} language default en
20922 * Create a new DateField
20923 * @param {Object} config The config object
20926 Roo.bootstrap.DateField = function(config){
20927 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20931 * Fires when this field show.
20932 * @param {Roo.bootstrap.DateField} this
20933 * @param {Mixed} date The date value
20938 * Fires when this field hide.
20939 * @param {Roo.bootstrap.DateField} this
20940 * @param {Mixed} date The date value
20945 * Fires when select a date.
20946 * @param {Roo.bootstrap.DateField} this
20947 * @param {Mixed} date The date value
20951 * @event beforeselect
20952 * Fires when before select a date.
20953 * @param {Roo.bootstrap.DateField} this
20954 * @param {Mixed} date The date value
20956 beforeselect : true
20960 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20963 * @cfg {String} format
20964 * The default date format string which can be overriden for localization support. The format must be
20965 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20969 * @cfg {String} altFormats
20970 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20971 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20973 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20981 todayHighlight : false,
20987 keyboardNavigation: true,
20989 calendarWeeks: false,
20991 startDate: -Infinity,
20995 daysOfWeekDisabled: [],
20999 singleMode : false,
21001 UTCDate: function()
21003 return new Date(Date.UTC.apply(Date, arguments));
21006 UTCToday: function()
21008 var today = new Date();
21009 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21012 getDate: function() {
21013 var d = this.getUTCDate();
21014 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21017 getUTCDate: function() {
21021 setDate: function(d) {
21022 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21025 setUTCDate: function(d) {
21027 this.setValue(this.formatDate(this.date));
21030 onRender: function(ct, position)
21033 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21035 this.language = this.language || 'en';
21036 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21037 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21039 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21040 this.format = this.format || 'm/d/y';
21041 this.isInline = false;
21042 this.isInput = true;
21043 this.component = this.el.select('.add-on', true).first() || false;
21044 this.component = (this.component && this.component.length === 0) ? false : this.component;
21045 this.hasInput = this.component && this.inputEl().length;
21047 if (typeof(this.minViewMode === 'string')) {
21048 switch (this.minViewMode) {
21050 this.minViewMode = 1;
21053 this.minViewMode = 2;
21056 this.minViewMode = 0;
21061 if (typeof(this.viewMode === 'string')) {
21062 switch (this.viewMode) {
21075 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21077 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21079 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21081 this.picker().on('mousedown', this.onMousedown, this);
21082 this.picker().on('click', this.onClick, this);
21084 this.picker().addClass('datepicker-dropdown');
21086 this.startViewMode = this.viewMode;
21088 if(this.singleMode){
21089 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21090 v.setVisibilityMode(Roo.Element.DISPLAY);
21094 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21095 v.setStyle('width', '189px');
21099 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21100 if(!this.calendarWeeks){
21105 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21106 v.attr('colspan', function(i, val){
21107 return parseInt(val) + 1;
21112 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21114 this.setStartDate(this.startDate);
21115 this.setEndDate(this.endDate);
21117 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21124 if(this.isInline) {
21129 picker : function()
21131 return this.pickerEl;
21132 // return this.el.select('.datepicker', true).first();
21135 fillDow: function()
21137 var dowCnt = this.weekStart;
21146 if(this.calendarWeeks){
21154 while (dowCnt < this.weekStart + 7) {
21158 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21162 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21165 fillMonths: function()
21168 var months = this.picker().select('>.datepicker-months td', true).first();
21170 months.dom.innerHTML = '';
21176 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21179 months.createChild(month);
21186 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;
21188 if (this.date < this.startDate) {
21189 this.viewDate = new Date(this.startDate);
21190 } else if (this.date > this.endDate) {
21191 this.viewDate = new Date(this.endDate);
21193 this.viewDate = new Date(this.date);
21201 var d = new Date(this.viewDate),
21202 year = d.getUTCFullYear(),
21203 month = d.getUTCMonth(),
21204 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21205 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21206 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21207 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21208 currentDate = this.date && this.date.valueOf(),
21209 today = this.UTCToday();
21211 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21213 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21215 // this.picker.select('>tfoot th.today').
21216 // .text(dates[this.language].today)
21217 // .toggle(this.todayBtn !== false);
21219 this.updateNavArrows();
21222 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21224 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21226 prevMonth.setUTCDate(day);
21228 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21230 var nextMonth = new Date(prevMonth);
21232 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21234 nextMonth = nextMonth.valueOf();
21236 var fillMonths = false;
21238 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21240 while(prevMonth.valueOf() <= nextMonth) {
21243 if (prevMonth.getUTCDay() === this.weekStart) {
21245 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21253 if(this.calendarWeeks){
21254 // ISO 8601: First week contains first thursday.
21255 // ISO also states week starts on Monday, but we can be more abstract here.
21257 // Start of current week: based on weekstart/current date
21258 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21259 // Thursday of this week
21260 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21261 // First Thursday of year, year from thursday
21262 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21263 // Calendar week: ms between thursdays, div ms per day, div 7 days
21264 calWeek = (th - yth) / 864e5 / 7 + 1;
21266 fillMonths.cn.push({
21274 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21276 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21279 if (this.todayHighlight &&
21280 prevMonth.getUTCFullYear() == today.getFullYear() &&
21281 prevMonth.getUTCMonth() == today.getMonth() &&
21282 prevMonth.getUTCDate() == today.getDate()) {
21283 clsName += ' today';
21286 if (currentDate && prevMonth.valueOf() === currentDate) {
21287 clsName += ' active';
21290 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21291 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21292 clsName += ' disabled';
21295 fillMonths.cn.push({
21297 cls: 'day ' + clsName,
21298 html: prevMonth.getDate()
21301 prevMonth.setDate(prevMonth.getDate()+1);
21304 var currentYear = this.date && this.date.getUTCFullYear();
21305 var currentMonth = this.date && this.date.getUTCMonth();
21307 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21309 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21310 v.removeClass('active');
21312 if(currentYear === year && k === currentMonth){
21313 v.addClass('active');
21316 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21317 v.addClass('disabled');
21323 year = parseInt(year/10, 10) * 10;
21325 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21327 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21330 for (var i = -1; i < 11; i++) {
21331 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21333 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21341 showMode: function(dir)
21344 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21347 Roo.each(this.picker().select('>div',true).elements, function(v){
21348 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21351 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21356 if(this.isInline) {
21360 this.picker().removeClass(['bottom', 'top']);
21362 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21364 * place to the top of element!
21368 this.picker().addClass('top');
21369 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21374 this.picker().addClass('bottom');
21376 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21379 parseDate : function(value)
21381 if(!value || value instanceof Date){
21384 var v = Date.parseDate(value, this.format);
21385 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21386 v = Date.parseDate(value, 'Y-m-d');
21388 if(!v && this.altFormats){
21389 if(!this.altFormatsArray){
21390 this.altFormatsArray = this.altFormats.split("|");
21392 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21393 v = Date.parseDate(value, this.altFormatsArray[i]);
21399 formatDate : function(date, fmt)
21401 return (!date || !(date instanceof Date)) ?
21402 date : date.dateFormat(fmt || this.format);
21405 onFocus : function()
21407 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21411 onBlur : function()
21413 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21415 var d = this.inputEl().getValue();
21422 showPopup : function()
21424 this.picker().show();
21428 this.fireEvent('showpopup', this, this.date);
21431 hidePopup : function()
21433 if(this.isInline) {
21436 this.picker().hide();
21437 this.viewMode = this.startViewMode;
21440 this.fireEvent('hidepopup', this, this.date);
21444 onMousedown: function(e)
21446 e.stopPropagation();
21447 e.preventDefault();
21452 Roo.bootstrap.DateField.superclass.keyup.call(this);
21456 setValue: function(v)
21458 if(this.fireEvent('beforeselect', this, v) !== false){
21459 var d = new Date(this.parseDate(v) ).clearTime();
21461 if(isNaN(d.getTime())){
21462 this.date = this.viewDate = '';
21463 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21467 v = this.formatDate(d);
21469 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21471 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21475 this.fireEvent('select', this, this.date);
21479 getValue: function()
21481 return this.formatDate(this.date);
21484 fireKey: function(e)
21486 if (!this.picker().isVisible()){
21487 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21493 var dateChanged = false,
21495 newDate, newViewDate;
21500 e.preventDefault();
21504 if (!this.keyboardNavigation) {
21507 dir = e.keyCode == 37 ? -1 : 1;
21510 newDate = this.moveYear(this.date, dir);
21511 newViewDate = this.moveYear(this.viewDate, dir);
21512 } else if (e.shiftKey){
21513 newDate = this.moveMonth(this.date, dir);
21514 newViewDate = this.moveMonth(this.viewDate, dir);
21516 newDate = new Date(this.date);
21517 newDate.setUTCDate(this.date.getUTCDate() + dir);
21518 newViewDate = new Date(this.viewDate);
21519 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21521 if (this.dateWithinRange(newDate)){
21522 this.date = newDate;
21523 this.viewDate = newViewDate;
21524 this.setValue(this.formatDate(this.date));
21526 e.preventDefault();
21527 dateChanged = true;
21532 if (!this.keyboardNavigation) {
21535 dir = e.keyCode == 38 ? -1 : 1;
21537 newDate = this.moveYear(this.date, dir);
21538 newViewDate = this.moveYear(this.viewDate, dir);
21539 } else if (e.shiftKey){
21540 newDate = this.moveMonth(this.date, dir);
21541 newViewDate = this.moveMonth(this.viewDate, dir);
21543 newDate = new Date(this.date);
21544 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21545 newViewDate = new Date(this.viewDate);
21546 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21548 if (this.dateWithinRange(newDate)){
21549 this.date = newDate;
21550 this.viewDate = newViewDate;
21551 this.setValue(this.formatDate(this.date));
21553 e.preventDefault();
21554 dateChanged = true;
21558 this.setValue(this.formatDate(this.date));
21560 e.preventDefault();
21563 this.setValue(this.formatDate(this.date));
21577 onClick: function(e)
21579 e.stopPropagation();
21580 e.preventDefault();
21582 var target = e.getTarget();
21584 if(target.nodeName.toLowerCase() === 'i'){
21585 target = Roo.get(target).dom.parentNode;
21588 var nodeName = target.nodeName;
21589 var className = target.className;
21590 var html = target.innerHTML;
21591 //Roo.log(nodeName);
21593 switch(nodeName.toLowerCase()) {
21595 switch(className) {
21601 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21602 switch(this.viewMode){
21604 this.viewDate = this.moveMonth(this.viewDate, dir);
21608 this.viewDate = this.moveYear(this.viewDate, dir);
21614 var date = new Date();
21615 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21617 this.setValue(this.formatDate(this.date));
21624 if (className.indexOf('disabled') < 0) {
21625 this.viewDate.setUTCDate(1);
21626 if (className.indexOf('month') > -1) {
21627 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21629 var year = parseInt(html, 10) || 0;
21630 this.viewDate.setUTCFullYear(year);
21634 if(this.singleMode){
21635 this.setValue(this.formatDate(this.viewDate));
21646 //Roo.log(className);
21647 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21648 var day = parseInt(html, 10) || 1;
21649 var year = (this.viewDate || new Date()).getUTCFullYear(),
21650 month = (this.viewDate || new Date()).getUTCMonth();
21652 if (className.indexOf('old') > -1) {
21659 } else if (className.indexOf('new') > -1) {
21667 //Roo.log([year,month,day]);
21668 this.date = this.UTCDate(year, month, day,0,0,0,0);
21669 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21671 //Roo.log(this.formatDate(this.date));
21672 this.setValue(this.formatDate(this.date));
21679 setStartDate: function(startDate)
21681 this.startDate = startDate || -Infinity;
21682 if (this.startDate !== -Infinity) {
21683 this.startDate = this.parseDate(this.startDate);
21686 this.updateNavArrows();
21689 setEndDate: function(endDate)
21691 this.endDate = endDate || Infinity;
21692 if (this.endDate !== Infinity) {
21693 this.endDate = this.parseDate(this.endDate);
21696 this.updateNavArrows();
21699 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21701 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21702 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21703 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21705 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21706 return parseInt(d, 10);
21709 this.updateNavArrows();
21712 updateNavArrows: function()
21714 if(this.singleMode){
21718 var d = new Date(this.viewDate),
21719 year = d.getUTCFullYear(),
21720 month = d.getUTCMonth();
21722 Roo.each(this.picker().select('.prev', true).elements, function(v){
21724 switch (this.viewMode) {
21727 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21733 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21740 Roo.each(this.picker().select('.next', true).elements, function(v){
21742 switch (this.viewMode) {
21745 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21751 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21759 moveMonth: function(date, dir)
21764 var new_date = new Date(date.valueOf()),
21765 day = new_date.getUTCDate(),
21766 month = new_date.getUTCMonth(),
21767 mag = Math.abs(dir),
21769 dir = dir > 0 ? 1 : -1;
21772 // If going back one month, make sure month is not current month
21773 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21775 return new_date.getUTCMonth() == month;
21777 // If going forward one month, make sure month is as expected
21778 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21780 return new_date.getUTCMonth() != new_month;
21782 new_month = month + dir;
21783 new_date.setUTCMonth(new_month);
21784 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21785 if (new_month < 0 || new_month > 11) {
21786 new_month = (new_month + 12) % 12;
21789 // For magnitudes >1, move one month at a time...
21790 for (var i=0; i<mag; i++) {
21791 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21792 new_date = this.moveMonth(new_date, dir);
21794 // ...then reset the day, keeping it in the new month
21795 new_month = new_date.getUTCMonth();
21796 new_date.setUTCDate(day);
21798 return new_month != new_date.getUTCMonth();
21801 // Common date-resetting loop -- if date is beyond end of month, make it
21804 new_date.setUTCDate(--day);
21805 new_date.setUTCMonth(new_month);
21810 moveYear: function(date, dir)
21812 return this.moveMonth(date, dir*12);
21815 dateWithinRange: function(date)
21817 return date >= this.startDate && date <= this.endDate;
21823 this.picker().remove();
21826 validateValue : function(value)
21828 if(this.getVisibilityEl().hasClass('hidden')){
21832 if(value.length < 1) {
21833 if(this.allowBlank){
21839 if(value.length < this.minLength){
21842 if(value.length > this.maxLength){
21846 var vt = Roo.form.VTypes;
21847 if(!vt[this.vtype](value, this)){
21851 if(typeof this.validator == "function"){
21852 var msg = this.validator(value);
21858 if(this.regex && !this.regex.test(value)){
21862 if(typeof(this.parseDate(value)) == 'undefined'){
21866 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21870 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21880 this.date = this.viewDate = '';
21882 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21887 Roo.apply(Roo.bootstrap.DateField, {
21898 html: '<i class="fa fa-arrow-left"/>'
21908 html: '<i class="fa fa-arrow-right"/>'
21950 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21951 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21952 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21953 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21954 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21967 navFnc: 'FullYear',
21972 navFnc: 'FullYear',
21977 Roo.apply(Roo.bootstrap.DateField, {
21981 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21985 cls: 'datepicker-days',
21989 cls: 'table-condensed',
21991 Roo.bootstrap.DateField.head,
21995 Roo.bootstrap.DateField.footer
22002 cls: 'datepicker-months',
22006 cls: 'table-condensed',
22008 Roo.bootstrap.DateField.head,
22009 Roo.bootstrap.DateField.content,
22010 Roo.bootstrap.DateField.footer
22017 cls: 'datepicker-years',
22021 cls: 'table-condensed',
22023 Roo.bootstrap.DateField.head,
22024 Roo.bootstrap.DateField.content,
22025 Roo.bootstrap.DateField.footer
22044 * @class Roo.bootstrap.TimeField
22045 * @extends Roo.bootstrap.Input
22046 * Bootstrap DateField class
22050 * Create a new TimeField
22051 * @param {Object} config The config object
22054 Roo.bootstrap.TimeField = function(config){
22055 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22059 * Fires when this field show.
22060 * @param {Roo.bootstrap.DateField} thisthis
22061 * @param {Mixed} date The date value
22066 * Fires when this field hide.
22067 * @param {Roo.bootstrap.DateField} this
22068 * @param {Mixed} date The date value
22073 * Fires when select a date.
22074 * @param {Roo.bootstrap.DateField} this
22075 * @param {Mixed} date The date value
22081 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22084 * @cfg {String} format
22085 * The default time format string which can be overriden for localization support. The format must be
22086 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22090 getAutoCreate : function()
22092 this.after = '<i class="fa far fa-clock"></i>';
22093 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22097 onRender: function(ct, position)
22100 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22102 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22104 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22106 this.pop = this.picker().select('>.datepicker-time',true).first();
22107 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22109 this.picker().on('mousedown', this.onMousedown, this);
22110 this.picker().on('click', this.onClick, this);
22112 this.picker().addClass('datepicker-dropdown');
22117 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22118 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22119 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22120 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22121 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22122 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22126 fireKey: function(e){
22127 if (!this.picker().isVisible()){
22128 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22134 e.preventDefault();
22142 this.onTogglePeriod();
22145 this.onIncrementMinutes();
22148 this.onDecrementMinutes();
22157 onClick: function(e) {
22158 e.stopPropagation();
22159 e.preventDefault();
22162 picker : function()
22164 return this.pickerEl;
22167 fillTime: function()
22169 var time = this.pop.select('tbody', true).first();
22171 time.dom.innerHTML = '';
22186 cls: 'hours-up fa fas fa-chevron-up'
22206 cls: 'minutes-up fa fas fa-chevron-up'
22227 cls: 'timepicker-hour',
22242 cls: 'timepicker-minute',
22257 cls: 'btn btn-primary period',
22279 cls: 'hours-down fa fas fa-chevron-down'
22299 cls: 'minutes-down fa fas fa-chevron-down'
22317 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22324 var hours = this.time.getHours();
22325 var minutes = this.time.getMinutes();
22338 hours = hours - 12;
22342 hours = '0' + hours;
22346 minutes = '0' + minutes;
22349 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22350 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22351 this.pop.select('button', true).first().dom.innerHTML = period;
22357 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22359 var cls = ['bottom'];
22361 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22368 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22372 //this.picker().setXY(20000,20000);
22373 this.picker().addClass(cls.join('-'));
22377 Roo.each(cls, function(c){
22382 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22383 //_this.picker().setTop(_this.inputEl().getHeight());
22387 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22389 //_this.picker().setTop(0 - _this.picker().getHeight());
22394 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22398 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22406 onFocus : function()
22408 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22412 onBlur : function()
22414 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22420 this.picker().show();
22425 this.fireEvent('show', this, this.date);
22430 this.picker().hide();
22433 this.fireEvent('hide', this, this.date);
22436 setTime : function()
22439 this.setValue(this.time.format(this.format));
22441 this.fireEvent('select', this, this.date);
22446 onMousedown: function(e){
22447 e.stopPropagation();
22448 e.preventDefault();
22451 onIncrementHours: function()
22453 Roo.log('onIncrementHours');
22454 this.time = this.time.add(Date.HOUR, 1);
22459 onDecrementHours: function()
22461 Roo.log('onDecrementHours');
22462 this.time = this.time.add(Date.HOUR, -1);
22466 onIncrementMinutes: function()
22468 Roo.log('onIncrementMinutes');
22469 this.time = this.time.add(Date.MINUTE, 1);
22473 onDecrementMinutes: function()
22475 Roo.log('onDecrementMinutes');
22476 this.time = this.time.add(Date.MINUTE, -1);
22480 onTogglePeriod: function()
22482 Roo.log('onTogglePeriod');
22483 this.time = this.time.add(Date.HOUR, 12);
22491 Roo.apply(Roo.bootstrap.TimeField, {
22495 cls: 'datepicker dropdown-menu',
22499 cls: 'datepicker-time',
22503 cls: 'table-condensed',
22532 cls: 'btn btn-info ok',
22560 * @class Roo.bootstrap.MonthField
22561 * @extends Roo.bootstrap.Input
22562 * Bootstrap MonthField class
22564 * @cfg {String} language default en
22567 * Create a new MonthField
22568 * @param {Object} config The config object
22571 Roo.bootstrap.MonthField = function(config){
22572 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22577 * Fires when this field show.
22578 * @param {Roo.bootstrap.MonthField} this
22579 * @param {Mixed} date The date value
22584 * Fires when this field hide.
22585 * @param {Roo.bootstrap.MonthField} this
22586 * @param {Mixed} date The date value
22591 * Fires when select a date.
22592 * @param {Roo.bootstrap.MonthField} this
22593 * @param {String} oldvalue The old value
22594 * @param {String} newvalue The new value
22600 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22602 onRender: function(ct, position)
22605 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22607 this.language = this.language || 'en';
22608 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22609 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22611 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22612 this.isInline = false;
22613 this.isInput = true;
22614 this.component = this.el.select('.add-on', true).first() || false;
22615 this.component = (this.component && this.component.length === 0) ? false : this.component;
22616 this.hasInput = this.component && this.inputEL().length;
22618 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22620 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22622 this.picker().on('mousedown', this.onMousedown, this);
22623 this.picker().on('click', this.onClick, this);
22625 this.picker().addClass('datepicker-dropdown');
22627 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22628 v.setStyle('width', '189px');
22635 if(this.isInline) {
22641 setValue: function(v, suppressEvent)
22643 var o = this.getValue();
22645 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22649 if(suppressEvent !== true){
22650 this.fireEvent('select', this, o, v);
22655 getValue: function()
22660 onClick: function(e)
22662 e.stopPropagation();
22663 e.preventDefault();
22665 var target = e.getTarget();
22667 if(target.nodeName.toLowerCase() === 'i'){
22668 target = Roo.get(target).dom.parentNode;
22671 var nodeName = target.nodeName;
22672 var className = target.className;
22673 var html = target.innerHTML;
22675 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22679 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22681 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22687 picker : function()
22689 return this.pickerEl;
22692 fillMonths: function()
22695 var months = this.picker().select('>.datepicker-months td', true).first();
22697 months.dom.innerHTML = '';
22703 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22706 months.createChild(month);
22715 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22716 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22719 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22720 e.removeClass('active');
22722 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22723 e.addClass('active');
22730 if(this.isInline) {
22734 this.picker().removeClass(['bottom', 'top']);
22736 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22738 * place to the top of element!
22742 this.picker().addClass('top');
22743 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22748 this.picker().addClass('bottom');
22750 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22753 onFocus : function()
22755 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22759 onBlur : function()
22761 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22763 var d = this.inputEl().getValue();
22772 this.picker().show();
22773 this.picker().select('>.datepicker-months', true).first().show();
22777 this.fireEvent('show', this, this.date);
22782 if(this.isInline) {
22785 this.picker().hide();
22786 this.fireEvent('hide', this, this.date);
22790 onMousedown: function(e)
22792 e.stopPropagation();
22793 e.preventDefault();
22798 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22802 fireKey: function(e)
22804 if (!this.picker().isVisible()){
22805 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22816 e.preventDefault();
22820 dir = e.keyCode == 37 ? -1 : 1;
22822 this.vIndex = this.vIndex + dir;
22824 if(this.vIndex < 0){
22828 if(this.vIndex > 11){
22832 if(isNaN(this.vIndex)){
22836 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22842 dir = e.keyCode == 38 ? -1 : 1;
22844 this.vIndex = this.vIndex + dir * 4;
22846 if(this.vIndex < 0){
22850 if(this.vIndex > 11){
22854 if(isNaN(this.vIndex)){
22858 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22863 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22864 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22868 e.preventDefault();
22871 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22872 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22888 this.picker().remove();
22893 Roo.apply(Roo.bootstrap.MonthField, {
22912 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22913 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22918 Roo.apply(Roo.bootstrap.MonthField, {
22922 cls: 'datepicker dropdown-menu roo-dynamic',
22926 cls: 'datepicker-months',
22930 cls: 'table-condensed',
22932 Roo.bootstrap.DateField.content
22952 * @class Roo.bootstrap.CheckBox
22953 * @extends Roo.bootstrap.Input
22954 * Bootstrap CheckBox class
22956 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22957 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22958 * @cfg {String} boxLabel The text that appears beside the checkbox
22959 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22960 * @cfg {Boolean} checked initnal the element
22961 * @cfg {Boolean} inline inline the element (default false)
22962 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22963 * @cfg {String} tooltip label tooltip
22966 * Create a new CheckBox
22967 * @param {Object} config The config object
22970 Roo.bootstrap.CheckBox = function(config){
22971 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22976 * Fires when the element is checked or unchecked.
22977 * @param {Roo.bootstrap.CheckBox} this This input
22978 * @param {Boolean} checked The new checked value
22983 * Fires when the element is click.
22984 * @param {Roo.bootstrap.CheckBox} this This input
22991 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22993 inputType: 'checkbox',
23002 // checkbox success does not make any sense really..
23007 getAutoCreate : function()
23009 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23015 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23018 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23024 type : this.inputType,
23025 value : this.inputValue,
23026 cls : 'roo-' + this.inputType, //'form-box',
23027 placeholder : this.placeholder || ''
23031 if(this.inputType != 'radio'){
23035 cls : 'roo-hidden-value',
23036 value : this.checked ? this.inputValue : this.valueOff
23041 if (this.weight) { // Validity check?
23042 cfg.cls += " " + this.inputType + "-" + this.weight;
23045 if (this.disabled) {
23046 input.disabled=true;
23050 input.checked = this.checked;
23055 input.name = this.name;
23057 if(this.inputType != 'radio'){
23058 hidden.name = this.name;
23059 input.name = '_hidden_' + this.name;
23064 input.cls += ' input-' + this.size;
23069 ['xs','sm','md','lg'].map(function(size){
23070 if (settings[size]) {
23071 cfg.cls += ' col-' + size + '-' + settings[size];
23075 var inputblock = input;
23077 if (this.before || this.after) {
23080 cls : 'input-group',
23085 inputblock.cn.push({
23087 cls : 'input-group-addon',
23092 inputblock.cn.push(input);
23094 if(this.inputType != 'radio'){
23095 inputblock.cn.push(hidden);
23099 inputblock.cn.push({
23101 cls : 'input-group-addon',
23107 var boxLabelCfg = false;
23113 //'for': id, // box label is handled by onclick - so no for...
23115 html: this.boxLabel
23118 boxLabelCfg.tooltip = this.tooltip;
23124 if (align ==='left' && this.fieldLabel.length) {
23125 // Roo.log("left and has label");
23130 cls : 'control-label',
23131 html : this.fieldLabel
23142 cfg.cn[1].cn.push(boxLabelCfg);
23145 if(this.labelWidth > 12){
23146 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23149 if(this.labelWidth < 13 && this.labelmd == 0){
23150 this.labelmd = this.labelWidth;
23153 if(this.labellg > 0){
23154 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23155 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23158 if(this.labelmd > 0){
23159 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23160 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23163 if(this.labelsm > 0){
23164 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23165 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23168 if(this.labelxs > 0){
23169 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23170 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23173 } else if ( this.fieldLabel.length) {
23174 // Roo.log(" label");
23178 tag: this.boxLabel ? 'span' : 'label',
23180 cls: 'control-label box-input-label',
23181 //cls : 'input-group-addon',
23182 html : this.fieldLabel
23189 cfg.cn.push(boxLabelCfg);
23194 // Roo.log(" no label && no align");
23195 cfg.cn = [ inputblock ] ;
23197 cfg.cn.push(boxLabelCfg);
23205 if(this.inputType != 'radio'){
23206 cfg.cn.push(hidden);
23214 * return the real input element.
23216 inputEl: function ()
23218 return this.el.select('input.roo-' + this.inputType,true).first();
23220 hiddenEl: function ()
23222 return this.el.select('input.roo-hidden-value',true).first();
23225 labelEl: function()
23227 return this.el.select('label.control-label',true).first();
23229 /* depricated... */
23233 return this.labelEl();
23236 boxLabelEl: function()
23238 return this.el.select('label.box-label',true).first();
23241 initEvents : function()
23243 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23245 this.inputEl().on('click', this.onClick, this);
23247 if (this.boxLabel) {
23248 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23251 this.startValue = this.getValue();
23254 Roo.bootstrap.CheckBox.register(this);
23258 onClick : function(e)
23260 if(this.fireEvent('click', this, e) !== false){
23261 this.setChecked(!this.checked);
23266 setChecked : function(state,suppressEvent)
23268 this.startValue = this.getValue();
23270 if(this.inputType == 'radio'){
23272 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23273 e.dom.checked = false;
23276 this.inputEl().dom.checked = true;
23278 this.inputEl().dom.value = this.inputValue;
23280 if(suppressEvent !== true){
23281 this.fireEvent('check', this, true);
23289 this.checked = state;
23291 this.inputEl().dom.checked = state;
23294 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23296 if(suppressEvent !== true){
23297 this.fireEvent('check', this, state);
23303 getValue : function()
23305 if(this.inputType == 'radio'){
23306 return this.getGroupValue();
23309 return this.hiddenEl().dom.value;
23313 getGroupValue : function()
23315 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23319 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23322 setValue : function(v,suppressEvent)
23324 if(this.inputType == 'radio'){
23325 this.setGroupValue(v, suppressEvent);
23329 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23334 setGroupValue : function(v, suppressEvent)
23336 this.startValue = this.getValue();
23338 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23339 e.dom.checked = false;
23341 if(e.dom.value == v){
23342 e.dom.checked = true;
23346 if(suppressEvent !== true){
23347 this.fireEvent('check', this, true);
23355 validate : function()
23357 if(this.getVisibilityEl().hasClass('hidden')){
23363 (this.inputType == 'radio' && this.validateRadio()) ||
23364 (this.inputType == 'checkbox' && this.validateCheckbox())
23370 this.markInvalid();
23374 validateRadio : function()
23376 if(this.getVisibilityEl().hasClass('hidden')){
23380 if(this.allowBlank){
23386 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23387 if(!e.dom.checked){
23399 validateCheckbox : function()
23402 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23403 //return (this.getValue() == this.inputValue) ? true : false;
23406 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23414 for(var i in group){
23415 if(group[i].el.isVisible(true)){
23423 for(var i in group){
23428 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23435 * Mark this field as valid
23437 markValid : function()
23441 this.fireEvent('valid', this);
23443 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23446 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23453 if(this.inputType == 'radio'){
23454 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23455 var fg = e.findParent('.form-group', false, true);
23456 if (Roo.bootstrap.version == 3) {
23457 fg.removeClass([_this.invalidClass, _this.validClass]);
23458 fg.addClass(_this.validClass);
23460 fg.removeClass(['is-valid', 'is-invalid']);
23461 fg.addClass('is-valid');
23469 var fg = this.el.findParent('.form-group', false, true);
23470 if (Roo.bootstrap.version == 3) {
23471 fg.removeClass([this.invalidClass, this.validClass]);
23472 fg.addClass(this.validClass);
23474 fg.removeClass(['is-valid', 'is-invalid']);
23475 fg.addClass('is-valid');
23480 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23486 for(var i in group){
23487 var fg = group[i].el.findParent('.form-group', false, true);
23488 if (Roo.bootstrap.version == 3) {
23489 fg.removeClass([this.invalidClass, this.validClass]);
23490 fg.addClass(this.validClass);
23492 fg.removeClass(['is-valid', 'is-invalid']);
23493 fg.addClass('is-valid');
23499 * Mark this field as invalid
23500 * @param {String} msg The validation message
23502 markInvalid : function(msg)
23504 if(this.allowBlank){
23510 this.fireEvent('invalid', this, msg);
23512 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23515 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23519 label.markInvalid();
23522 if(this.inputType == 'radio'){
23524 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23525 var fg = e.findParent('.form-group', false, true);
23526 if (Roo.bootstrap.version == 3) {
23527 fg.removeClass([_this.invalidClass, _this.validClass]);
23528 fg.addClass(_this.invalidClass);
23530 fg.removeClass(['is-invalid', 'is-valid']);
23531 fg.addClass('is-invalid');
23539 var fg = this.el.findParent('.form-group', false, true);
23540 if (Roo.bootstrap.version == 3) {
23541 fg.removeClass([_this.invalidClass, _this.validClass]);
23542 fg.addClass(_this.invalidClass);
23544 fg.removeClass(['is-invalid', 'is-valid']);
23545 fg.addClass('is-invalid');
23550 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23556 for(var i in group){
23557 var fg = group[i].el.findParent('.form-group', false, true);
23558 if (Roo.bootstrap.version == 3) {
23559 fg.removeClass([_this.invalidClass, _this.validClass]);
23560 fg.addClass(_this.invalidClass);
23562 fg.removeClass(['is-invalid', 'is-valid']);
23563 fg.addClass('is-invalid');
23569 clearInvalid : function()
23571 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23573 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23575 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23577 if (label && label.iconEl) {
23578 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23579 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23583 disable : function()
23585 if(this.inputType != 'radio'){
23586 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23593 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23594 _this.getActionEl().addClass(this.disabledClass);
23595 e.dom.disabled = true;
23599 this.disabled = true;
23600 this.fireEvent("disable", this);
23604 enable : function()
23606 if(this.inputType != 'radio'){
23607 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23614 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23615 _this.getActionEl().removeClass(this.disabledClass);
23616 e.dom.disabled = false;
23620 this.disabled = false;
23621 this.fireEvent("enable", this);
23625 setBoxLabel : function(v)
23630 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23636 Roo.apply(Roo.bootstrap.CheckBox, {
23641 * register a CheckBox Group
23642 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23644 register : function(checkbox)
23646 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23647 this.groups[checkbox.groupId] = {};
23650 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23654 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23658 * fetch a CheckBox Group based on the group ID
23659 * @param {string} the group ID
23660 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23662 get: function(groupId) {
23663 if (typeof(this.groups[groupId]) == 'undefined') {
23667 return this.groups[groupId] ;
23680 * @class Roo.bootstrap.Radio
23681 * @extends Roo.bootstrap.Component
23682 * Bootstrap Radio class
23683 * @cfg {String} boxLabel - the label associated
23684 * @cfg {String} value - the value of radio
23687 * Create a new Radio
23688 * @param {Object} config The config object
23690 Roo.bootstrap.Radio = function(config){
23691 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23695 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23701 getAutoCreate : function()
23705 cls : 'form-group radio',
23710 html : this.boxLabel
23718 initEvents : function()
23720 this.parent().register(this);
23722 this.el.on('click', this.onClick, this);
23726 onClick : function(e)
23728 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23729 this.setChecked(true);
23733 setChecked : function(state, suppressEvent)
23735 this.parent().setValue(this.value, suppressEvent);
23739 setBoxLabel : function(v)
23744 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23759 * @class Roo.bootstrap.SecurePass
23760 * @extends Roo.bootstrap.Input
23761 * Bootstrap SecurePass class
23765 * Create a new SecurePass
23766 * @param {Object} config The config object
23769 Roo.bootstrap.SecurePass = function (config) {
23770 // these go here, so the translation tool can replace them..
23772 PwdEmpty: "Please type a password, and then retype it to confirm.",
23773 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23774 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23775 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23776 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23777 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23778 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23779 TooWeak: "Your password is Too Weak."
23781 this.meterLabel = "Password strength:";
23782 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23783 this.meterClass = [
23784 "roo-password-meter-tooweak",
23785 "roo-password-meter-weak",
23786 "roo-password-meter-medium",
23787 "roo-password-meter-strong",
23788 "roo-password-meter-grey"
23793 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23796 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23798 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23800 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23801 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23802 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23803 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23804 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23805 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23806 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23816 * @cfg {String/Object} Label for the strength meter (defaults to
23817 * 'Password strength:')
23822 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23823 * ['Weak', 'Medium', 'Strong'])
23826 pwdStrengths: false,
23839 initEvents: function ()
23841 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23843 if (this.el.is('input[type=password]') && Roo.isSafari) {
23844 this.el.on('keydown', this.SafariOnKeyDown, this);
23847 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23850 onRender: function (ct, position)
23852 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23853 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23854 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23856 this.trigger.createChild({
23861 cls: 'roo-password-meter-grey col-xs-12',
23864 //width: this.meterWidth + 'px'
23868 cls: 'roo-password-meter-text'
23874 if (this.hideTrigger) {
23875 this.trigger.setDisplayed(false);
23877 this.setSize(this.width || '', this.height || '');
23880 onDestroy: function ()
23882 if (this.trigger) {
23883 this.trigger.removeAllListeners();
23884 this.trigger.remove();
23887 this.wrap.remove();
23889 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23892 checkStrength: function ()
23894 var pwd = this.inputEl().getValue();
23895 if (pwd == this._lastPwd) {
23900 if (this.ClientSideStrongPassword(pwd)) {
23902 } else if (this.ClientSideMediumPassword(pwd)) {
23904 } else if (this.ClientSideWeakPassword(pwd)) {
23910 Roo.log('strength1: ' + strength);
23912 //var pm = this.trigger.child('div/div/div').dom;
23913 var pm = this.trigger.child('div/div');
23914 pm.removeClass(this.meterClass);
23915 pm.addClass(this.meterClass[strength]);
23918 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23920 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23922 this._lastPwd = pwd;
23926 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23928 this._lastPwd = '';
23930 var pm = this.trigger.child('div/div');
23931 pm.removeClass(this.meterClass);
23932 pm.addClass('roo-password-meter-grey');
23935 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23938 this.inputEl().dom.type='password';
23941 validateValue: function (value)
23943 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23946 if (value.length == 0) {
23947 if (this.allowBlank) {
23948 this.clearInvalid();
23952 this.markInvalid(this.errors.PwdEmpty);
23953 this.errorMsg = this.errors.PwdEmpty;
23961 if (!value.match(/[\x21-\x7e]+/)) {
23962 this.markInvalid(this.errors.PwdBadChar);
23963 this.errorMsg = this.errors.PwdBadChar;
23966 if (value.length < 6) {
23967 this.markInvalid(this.errors.PwdShort);
23968 this.errorMsg = this.errors.PwdShort;
23971 if (value.length > 16) {
23972 this.markInvalid(this.errors.PwdLong);
23973 this.errorMsg = this.errors.PwdLong;
23977 if (this.ClientSideStrongPassword(value)) {
23979 } else if (this.ClientSideMediumPassword(value)) {
23981 } else if (this.ClientSideWeakPassword(value)) {
23988 if (strength < 2) {
23989 //this.markInvalid(this.errors.TooWeak);
23990 this.errorMsg = this.errors.TooWeak;
23995 console.log('strength2: ' + strength);
23997 //var pm = this.trigger.child('div/div/div').dom;
23999 var pm = this.trigger.child('div/div');
24000 pm.removeClass(this.meterClass);
24001 pm.addClass(this.meterClass[strength]);
24003 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24005 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24007 this.errorMsg = '';
24011 CharacterSetChecks: function (type)
24014 this.fResult = false;
24017 isctype: function (character, type)
24020 case this.kCapitalLetter:
24021 if (character >= 'A' && character <= 'Z') {
24026 case this.kSmallLetter:
24027 if (character >= 'a' && character <= 'z') {
24033 if (character >= '0' && character <= '9') {
24038 case this.kPunctuation:
24039 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24050 IsLongEnough: function (pwd, size)
24052 return !(pwd == null || isNaN(size) || pwd.length < size);
24055 SpansEnoughCharacterSets: function (word, nb)
24057 if (!this.IsLongEnough(word, nb))
24062 var characterSetChecks = new Array(
24063 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24064 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24067 for (var index = 0; index < word.length; ++index) {
24068 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24069 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24070 characterSetChecks[nCharSet].fResult = true;
24077 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24078 if (characterSetChecks[nCharSet].fResult) {
24083 if (nCharSets < nb) {
24089 ClientSideStrongPassword: function (pwd)
24091 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24094 ClientSideMediumPassword: function (pwd)
24096 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24099 ClientSideWeakPassword: function (pwd)
24101 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24104 })//<script type="text/javascript">
24107 * Based Ext JS Library 1.1.1
24108 * Copyright(c) 2006-2007, Ext JS, LLC.
24114 * @class Roo.HtmlEditorCore
24115 * @extends Roo.Component
24116 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24118 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24121 Roo.HtmlEditorCore = function(config){
24124 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24129 * @event initialize
24130 * Fires when the editor is fully initialized (including the iframe)
24131 * @param {Roo.HtmlEditorCore} this
24136 * Fires when the editor is first receives the focus. Any insertion must wait
24137 * until after this event.
24138 * @param {Roo.HtmlEditorCore} this
24142 * @event beforesync
24143 * Fires before the textarea is updated with content from the editor iframe. Return false
24144 * to cancel the sync.
24145 * @param {Roo.HtmlEditorCore} this
24146 * @param {String} html
24150 * @event beforepush
24151 * Fires before the iframe editor is updated with content from the textarea. Return false
24152 * to cancel the push.
24153 * @param {Roo.HtmlEditorCore} this
24154 * @param {String} html
24159 * Fires when the textarea is updated with content from the editor iframe.
24160 * @param {Roo.HtmlEditorCore} this
24161 * @param {String} html
24166 * Fires when the iframe editor is updated with content from the textarea.
24167 * @param {Roo.HtmlEditorCore} this
24168 * @param {String} html
24173 * @event editorevent
24174 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24175 * @param {Roo.HtmlEditorCore} this
24181 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24183 // defaults : white / black...
24184 this.applyBlacklists();
24191 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24195 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24201 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24206 * @cfg {Number} height (in pixels)
24210 * @cfg {Number} width (in pixels)
24215 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24218 stylesheets: false,
24223 // private properties
24224 validationEvent : false,
24226 initialized : false,
24228 sourceEditMode : false,
24229 onFocus : Roo.emptyFn,
24231 hideMode:'offsets',
24235 // blacklist + whitelisted elements..
24242 * Protected method that will not generally be called directly. It
24243 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24244 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24246 getDocMarkup : function(){
24250 // inherit styels from page...??
24251 if (this.stylesheets === false) {
24253 Roo.get(document.head).select('style').each(function(node) {
24254 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24257 Roo.get(document.head).select('link').each(function(node) {
24258 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24261 } else if (!this.stylesheets.length) {
24263 st = '<style type="text/css">' +
24264 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24267 for (var i in this.stylesheets) {
24268 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24273 st += '<style type="text/css">' +
24274 'IMG { cursor: pointer } ' +
24277 var cls = 'roo-htmleditor-body';
24279 if(this.bodyCls.length){
24280 cls += ' ' + this.bodyCls;
24283 return '<html><head>' + st +
24284 //<style type="text/css">' +
24285 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24287 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24291 onRender : function(ct, position)
24294 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24295 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24298 this.el.dom.style.border = '0 none';
24299 this.el.dom.setAttribute('tabIndex', -1);
24300 this.el.addClass('x-hidden hide');
24304 if(Roo.isIE){ // fix IE 1px bogus margin
24305 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24309 this.frameId = Roo.id();
24313 var iframe = this.owner.wrap.createChild({
24315 cls: 'form-control', // bootstrap..
24317 name: this.frameId,
24318 frameBorder : 'no',
24319 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24324 this.iframe = iframe.dom;
24326 this.assignDocWin();
24328 this.doc.designMode = 'on';
24331 this.doc.write(this.getDocMarkup());
24335 var task = { // must defer to wait for browser to be ready
24337 //console.log("run task?" + this.doc.readyState);
24338 this.assignDocWin();
24339 if(this.doc.body || this.doc.readyState == 'complete'){
24341 this.doc.designMode="on";
24345 Roo.TaskMgr.stop(task);
24346 this.initEditor.defer(10, this);
24353 Roo.TaskMgr.start(task);
24358 onResize : function(w, h)
24360 Roo.log('resize: ' +w + ',' + h );
24361 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24365 if(typeof w == 'number'){
24367 this.iframe.style.width = w + 'px';
24369 if(typeof h == 'number'){
24371 this.iframe.style.height = h + 'px';
24373 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24380 * Toggles the editor between standard and source edit mode.
24381 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24383 toggleSourceEdit : function(sourceEditMode){
24385 this.sourceEditMode = sourceEditMode === true;
24387 if(this.sourceEditMode){
24389 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24392 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24393 //this.iframe.className = '';
24396 //this.setSize(this.owner.wrap.getSize());
24397 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24404 * Protected method that will not generally be called directly. If you need/want
24405 * custom HTML cleanup, this is the method you should override.
24406 * @param {String} html The HTML to be cleaned
24407 * return {String} The cleaned HTML
24409 cleanHtml : function(html){
24410 html = String(html);
24411 if(html.length > 5){
24412 if(Roo.isSafari){ // strip safari nonsense
24413 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24416 if(html == ' '){
24423 * HTML Editor -> Textarea
24424 * Protected method that will not generally be called directly. Syncs the contents
24425 * of the editor iframe with the textarea.
24427 syncValue : function(){
24428 if(this.initialized){
24429 var bd = (this.doc.body || this.doc.documentElement);
24430 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24431 var html = bd.innerHTML;
24433 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24434 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24436 html = '<div style="'+m[0]+'">' + html + '</div>';
24439 html = this.cleanHtml(html);
24440 // fix up the special chars.. normaly like back quotes in word...
24441 // however we do not want to do this with chinese..
24442 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24444 var cc = match.charCodeAt();
24446 // Get the character value, handling surrogate pairs
24447 if (match.length == 2) {
24448 // It's a surrogate pair, calculate the Unicode code point
24449 var high = match.charCodeAt(0) - 0xD800;
24450 var low = match.charCodeAt(1) - 0xDC00;
24451 cc = (high * 0x400) + low + 0x10000;
24453 (cc >= 0x4E00 && cc < 0xA000 ) ||
24454 (cc >= 0x3400 && cc < 0x4E00 ) ||
24455 (cc >= 0xf900 && cc < 0xfb00 )
24460 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24461 return "&#" + cc + ";";
24468 if(this.owner.fireEvent('beforesync', this, html) !== false){
24469 this.el.dom.value = html;
24470 this.owner.fireEvent('sync', this, html);
24476 * Protected method that will not generally be called directly. Pushes the value of the textarea
24477 * into the iframe editor.
24479 pushValue : function(){
24480 if(this.initialized){
24481 var v = this.el.dom.value.trim();
24483 // if(v.length < 1){
24487 if(this.owner.fireEvent('beforepush', this, v) !== false){
24488 var d = (this.doc.body || this.doc.documentElement);
24490 this.cleanUpPaste();
24491 this.el.dom.value = d.innerHTML;
24492 this.owner.fireEvent('push', this, v);
24498 deferFocus : function(){
24499 this.focus.defer(10, this);
24503 focus : function(){
24504 if(this.win && !this.sourceEditMode){
24511 assignDocWin: function()
24513 var iframe = this.iframe;
24516 this.doc = iframe.contentWindow.document;
24517 this.win = iframe.contentWindow;
24519 // if (!Roo.get(this.frameId)) {
24522 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24523 // this.win = Roo.get(this.frameId).dom.contentWindow;
24525 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24529 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24530 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24535 initEditor : function(){
24536 //console.log("INIT EDITOR");
24537 this.assignDocWin();
24541 this.doc.designMode="on";
24543 this.doc.write(this.getDocMarkup());
24546 var dbody = (this.doc.body || this.doc.documentElement);
24547 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24548 // this copies styles from the containing element into thsi one..
24549 // not sure why we need all of this..
24550 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24552 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24553 //ss['background-attachment'] = 'fixed'; // w3c
24554 dbody.bgProperties = 'fixed'; // ie
24555 //Roo.DomHelper.applyStyles(dbody, ss);
24556 Roo.EventManager.on(this.doc, {
24557 //'mousedown': this.onEditorEvent,
24558 'mouseup': this.onEditorEvent,
24559 'dblclick': this.onEditorEvent,
24560 'click': this.onEditorEvent,
24561 'keyup': this.onEditorEvent,
24566 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24568 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24569 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24571 this.initialized = true;
24573 this.owner.fireEvent('initialize', this);
24578 onDestroy : function(){
24584 //for (var i =0; i < this.toolbars.length;i++) {
24585 // // fixme - ask toolbars for heights?
24586 // this.toolbars[i].onDestroy();
24589 //this.wrap.dom.innerHTML = '';
24590 //this.wrap.remove();
24595 onFirstFocus : function(){
24597 this.assignDocWin();
24600 this.activated = true;
24603 if(Roo.isGecko){ // prevent silly gecko errors
24605 var s = this.win.getSelection();
24606 if(!s.focusNode || s.focusNode.nodeType != 3){
24607 var r = s.getRangeAt(0);
24608 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24613 this.execCmd('useCSS', true);
24614 this.execCmd('styleWithCSS', false);
24617 this.owner.fireEvent('activate', this);
24621 adjustFont: function(btn){
24622 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24623 //if(Roo.isSafari){ // safari
24626 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24627 if(Roo.isSafari){ // safari
24628 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24629 v = (v < 10) ? 10 : v;
24630 v = (v > 48) ? 48 : v;
24631 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24636 v = Math.max(1, v+adjust);
24638 this.execCmd('FontSize', v );
24641 onEditorEvent : function(e)
24643 this.owner.fireEvent('editorevent', this, e);
24644 // this.updateToolbar();
24645 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24648 insertTag : function(tg)
24650 // could be a bit smarter... -> wrap the current selected tRoo..
24651 if (tg.toLowerCase() == 'span' ||
24652 tg.toLowerCase() == 'code' ||
24653 tg.toLowerCase() == 'sup' ||
24654 tg.toLowerCase() == 'sub'
24657 range = this.createRange(this.getSelection());
24658 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24659 wrappingNode.appendChild(range.extractContents());
24660 range.insertNode(wrappingNode);
24667 this.execCmd("formatblock", tg);
24671 insertText : function(txt)
24675 var range = this.createRange();
24676 range.deleteContents();
24677 //alert(Sender.getAttribute('label'));
24679 range.insertNode(this.doc.createTextNode(txt));
24685 * Executes a Midas editor command on the editor document and performs necessary focus and
24686 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24687 * @param {String} cmd The Midas command
24688 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24690 relayCmd : function(cmd, value){
24692 this.execCmd(cmd, value);
24693 this.owner.fireEvent('editorevent', this);
24694 //this.updateToolbar();
24695 this.owner.deferFocus();
24699 * Executes a Midas editor command directly on the editor document.
24700 * For visual commands, you should use {@link #relayCmd} instead.
24701 * <b>This should only be called after the editor is initialized.</b>
24702 * @param {String} cmd The Midas command
24703 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24705 execCmd : function(cmd, value){
24706 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24713 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24715 * @param {String} text | dom node..
24717 insertAtCursor : function(text)
24720 if(!this.activated){
24726 var r = this.doc.selection.createRange();
24737 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24741 // from jquery ui (MIT licenced)
24743 var win = this.win;
24745 if (win.getSelection && win.getSelection().getRangeAt) {
24746 range = win.getSelection().getRangeAt(0);
24747 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24748 range.insertNode(node);
24749 } else if (win.document.selection && win.document.selection.createRange) {
24750 // no firefox support
24751 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24752 win.document.selection.createRange().pasteHTML(txt);
24754 // no firefox support
24755 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24756 this.execCmd('InsertHTML', txt);
24765 mozKeyPress : function(e){
24767 var c = e.getCharCode(), cmd;
24770 c = String.fromCharCode(c).toLowerCase();
24784 this.cleanUpPaste.defer(100, this);
24792 e.preventDefault();
24800 fixKeys : function(){ // load time branching for fastest keydown performance
24802 return function(e){
24803 var k = e.getKey(), r;
24806 r = this.doc.selection.createRange();
24809 r.pasteHTML('    ');
24816 r = this.doc.selection.createRange();
24818 var target = r.parentElement();
24819 if(!target || target.tagName.toLowerCase() != 'li'){
24821 r.pasteHTML('<br />');
24827 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24828 this.cleanUpPaste.defer(100, this);
24834 }else if(Roo.isOpera){
24835 return function(e){
24836 var k = e.getKey();
24840 this.execCmd('InsertHTML','    ');
24843 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24844 this.cleanUpPaste.defer(100, this);
24849 }else if(Roo.isSafari){
24850 return function(e){
24851 var k = e.getKey();
24855 this.execCmd('InsertText','\t');
24859 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24860 this.cleanUpPaste.defer(100, this);
24868 getAllAncestors: function()
24870 var p = this.getSelectedNode();
24873 a.push(p); // push blank onto stack..
24874 p = this.getParentElement();
24878 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24882 a.push(this.doc.body);
24886 lastSelNode : false,
24889 getSelection : function()
24891 this.assignDocWin();
24892 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24895 getSelectedNode: function()
24897 // this may only work on Gecko!!!
24899 // should we cache this!!!!
24904 var range = this.createRange(this.getSelection()).cloneRange();
24907 var parent = range.parentElement();
24909 var testRange = range.duplicate();
24910 testRange.moveToElementText(parent);
24911 if (testRange.inRange(range)) {
24914 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24917 parent = parent.parentElement;
24922 // is ancestor a text element.
24923 var ac = range.commonAncestorContainer;
24924 if (ac.nodeType == 3) {
24925 ac = ac.parentNode;
24928 var ar = ac.childNodes;
24931 var other_nodes = [];
24932 var has_other_nodes = false;
24933 for (var i=0;i<ar.length;i++) {
24934 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24937 // fullly contained node.
24939 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24944 // probably selected..
24945 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24946 other_nodes.push(ar[i]);
24950 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24955 has_other_nodes = true;
24957 if (!nodes.length && other_nodes.length) {
24958 nodes= other_nodes;
24960 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24966 createRange: function(sel)
24968 // this has strange effects when using with
24969 // top toolbar - not sure if it's a great idea.
24970 //this.editor.contentWindow.focus();
24971 if (typeof sel != "undefined") {
24973 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24975 return this.doc.createRange();
24978 return this.doc.createRange();
24981 getParentElement: function()
24984 this.assignDocWin();
24985 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24987 var range = this.createRange(sel);
24990 var p = range.commonAncestorContainer;
24991 while (p.nodeType == 3) { // text node
25002 * Range intersection.. the hard stuff...
25006 * [ -- selected range --- ]
25010 * if end is before start or hits it. fail.
25011 * if start is after end or hits it fail.
25013 * if either hits (but other is outside. - then it's not
25019 // @see http://www.thismuchiknow.co.uk/?p=64.
25020 rangeIntersectsNode : function(range, node)
25022 var nodeRange = node.ownerDocument.createRange();
25024 nodeRange.selectNode(node);
25026 nodeRange.selectNodeContents(node);
25029 var rangeStartRange = range.cloneRange();
25030 rangeStartRange.collapse(true);
25032 var rangeEndRange = range.cloneRange();
25033 rangeEndRange.collapse(false);
25035 var nodeStartRange = nodeRange.cloneRange();
25036 nodeStartRange.collapse(true);
25038 var nodeEndRange = nodeRange.cloneRange();
25039 nodeEndRange.collapse(false);
25041 return rangeStartRange.compareBoundaryPoints(
25042 Range.START_TO_START, nodeEndRange) == -1 &&
25043 rangeEndRange.compareBoundaryPoints(
25044 Range.START_TO_START, nodeStartRange) == 1;
25048 rangeCompareNode : function(range, node)
25050 var nodeRange = node.ownerDocument.createRange();
25052 nodeRange.selectNode(node);
25054 nodeRange.selectNodeContents(node);
25058 range.collapse(true);
25060 nodeRange.collapse(true);
25062 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25063 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25065 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25067 var nodeIsBefore = ss == 1;
25068 var nodeIsAfter = ee == -1;
25070 if (nodeIsBefore && nodeIsAfter) {
25073 if (!nodeIsBefore && nodeIsAfter) {
25074 return 1; //right trailed.
25077 if (nodeIsBefore && !nodeIsAfter) {
25078 return 2; // left trailed.
25084 // private? - in a new class?
25085 cleanUpPaste : function()
25087 // cleans up the whole document..
25088 Roo.log('cleanuppaste');
25090 this.cleanUpChildren(this.doc.body);
25091 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25092 if (clean != this.doc.body.innerHTML) {
25093 this.doc.body.innerHTML = clean;
25098 cleanWordChars : function(input) {// change the chars to hex code
25099 var he = Roo.HtmlEditorCore;
25101 var output = input;
25102 Roo.each(he.swapCodes, function(sw) {
25103 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25105 output = output.replace(swapper, sw[1]);
25112 cleanUpChildren : function (n)
25114 if (!n.childNodes.length) {
25117 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25118 this.cleanUpChild(n.childNodes[i]);
25125 cleanUpChild : function (node)
25128 //console.log(node);
25129 if (node.nodeName == "#text") {
25130 // clean up silly Windows -- stuff?
25133 if (node.nodeName == "#comment") {
25134 node.parentNode.removeChild(node);
25135 // clean up silly Windows -- stuff?
25138 var lcname = node.tagName.toLowerCase();
25139 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25140 // whitelist of tags..
25142 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25144 node.parentNode.removeChild(node);
25149 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25151 // spans with no attributes - just remove them..
25152 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25153 remove_keep_children = true;
25156 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25157 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25159 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25160 // remove_keep_children = true;
25163 if (remove_keep_children) {
25164 this.cleanUpChildren(node);
25165 // inserts everything just before this node...
25166 while (node.childNodes.length) {
25167 var cn = node.childNodes[0];
25168 node.removeChild(cn);
25169 node.parentNode.insertBefore(cn, node);
25171 node.parentNode.removeChild(node);
25175 if (!node.attributes || !node.attributes.length) {
25180 this.cleanUpChildren(node);
25184 function cleanAttr(n,v)
25187 if (v.match(/^\./) || v.match(/^\//)) {
25190 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25193 if (v.match(/^#/)) {
25196 if (v.match(/^\{/)) { // allow template editing.
25199 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25200 node.removeAttribute(n);
25204 var cwhite = this.cwhite;
25205 var cblack = this.cblack;
25207 function cleanStyle(n,v)
25209 if (v.match(/expression/)) { //XSS?? should we even bother..
25210 node.removeAttribute(n);
25214 var parts = v.split(/;/);
25217 Roo.each(parts, function(p) {
25218 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25222 var l = p.split(':').shift().replace(/\s+/g,'');
25223 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25225 if ( cwhite.length && cblack.indexOf(l) > -1) {
25226 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25227 //node.removeAttribute(n);
25231 // only allow 'c whitelisted system attributes'
25232 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25233 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25234 //node.removeAttribute(n);
25244 if (clean.length) {
25245 node.setAttribute(n, clean.join(';'));
25247 node.removeAttribute(n);
25253 for (var i = node.attributes.length-1; i > -1 ; i--) {
25254 var a = node.attributes[i];
25257 if (a.name.toLowerCase().substr(0,2)=='on') {
25258 node.removeAttribute(a.name);
25261 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25262 node.removeAttribute(a.name);
25265 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25266 cleanAttr(a.name,a.value); // fixme..
25269 if (a.name == 'style') {
25270 cleanStyle(a.name,a.value);
25273 /// clean up MS crap..
25274 // tecnically this should be a list of valid class'es..
25277 if (a.name == 'class') {
25278 if (a.value.match(/^Mso/)) {
25279 node.removeAttribute('class');
25282 if (a.value.match(/^body$/)) {
25283 node.removeAttribute('class');
25294 this.cleanUpChildren(node);
25300 * Clean up MS wordisms...
25302 cleanWord : function(node)
25305 this.cleanWord(this.doc.body);
25310 node.nodeName == 'SPAN' &&
25311 !node.hasAttributes() &&
25312 node.childNodes.length == 1 &&
25313 node.firstChild.nodeName == "#text"
25315 var textNode = node.firstChild;
25316 node.removeChild(textNode);
25317 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25318 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25320 node.parentNode.insertBefore(textNode, node);
25321 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25322 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25324 node.parentNode.removeChild(node);
25327 if (node.nodeName == "#text") {
25328 // clean up silly Windows -- stuff?
25331 if (node.nodeName == "#comment") {
25332 node.parentNode.removeChild(node);
25333 // clean up silly Windows -- stuff?
25337 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25338 node.parentNode.removeChild(node);
25341 //Roo.log(node.tagName);
25342 // remove - but keep children..
25343 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25344 //Roo.log('-- removed');
25345 while (node.childNodes.length) {
25346 var cn = node.childNodes[0];
25347 node.removeChild(cn);
25348 node.parentNode.insertBefore(cn, node);
25349 // move node to parent - and clean it..
25350 this.cleanWord(cn);
25352 node.parentNode.removeChild(node);
25353 /// no need to iterate chidlren = it's got none..
25354 //this.iterateChildren(node, this.cleanWord);
25358 if (node.className.length) {
25360 var cn = node.className.split(/\W+/);
25362 Roo.each(cn, function(cls) {
25363 if (cls.match(/Mso[a-zA-Z]+/)) {
25368 node.className = cna.length ? cna.join(' ') : '';
25370 node.removeAttribute("class");
25374 if (node.hasAttribute("lang")) {
25375 node.removeAttribute("lang");
25378 if (node.hasAttribute("style")) {
25380 var styles = node.getAttribute("style").split(";");
25382 Roo.each(styles, function(s) {
25383 if (!s.match(/:/)) {
25386 var kv = s.split(":");
25387 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25390 // what ever is left... we allow.
25393 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25394 if (!nstyle.length) {
25395 node.removeAttribute('style');
25398 this.iterateChildren(node, this.cleanWord);
25404 * iterateChildren of a Node, calling fn each time, using this as the scole..
25405 * @param {DomNode} node node to iterate children of.
25406 * @param {Function} fn method of this class to call on each item.
25408 iterateChildren : function(node, fn)
25410 if (!node.childNodes.length) {
25413 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25414 fn.call(this, node.childNodes[i])
25420 * cleanTableWidths.
25422 * Quite often pasting from word etc.. results in tables with column and widths.
25423 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25426 cleanTableWidths : function(node)
25431 this.cleanTableWidths(this.doc.body);
25436 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25439 Roo.log(node.tagName);
25440 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25441 this.iterateChildren(node, this.cleanTableWidths);
25444 if (node.hasAttribute('width')) {
25445 node.removeAttribute('width');
25449 if (node.hasAttribute("style")) {
25452 var styles = node.getAttribute("style").split(";");
25454 Roo.each(styles, function(s) {
25455 if (!s.match(/:/)) {
25458 var kv = s.split(":");
25459 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25462 // what ever is left... we allow.
25465 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25466 if (!nstyle.length) {
25467 node.removeAttribute('style');
25471 this.iterateChildren(node, this.cleanTableWidths);
25479 domToHTML : function(currentElement, depth, nopadtext) {
25481 depth = depth || 0;
25482 nopadtext = nopadtext || false;
25484 if (!currentElement) {
25485 return this.domToHTML(this.doc.body);
25488 //Roo.log(currentElement);
25490 var allText = false;
25491 var nodeName = currentElement.nodeName;
25492 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25494 if (nodeName == '#text') {
25496 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25501 if (nodeName != 'BODY') {
25504 // Prints the node tagName, such as <A>, <IMG>, etc
25507 for(i = 0; i < currentElement.attributes.length;i++) {
25509 var aname = currentElement.attributes.item(i).name;
25510 if (!currentElement.attributes.item(i).value.length) {
25513 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25516 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25525 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25528 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25533 // Traverse the tree
25535 var currentElementChild = currentElement.childNodes.item(i);
25536 var allText = true;
25537 var innerHTML = '';
25539 while (currentElementChild) {
25540 // Formatting code (indent the tree so it looks nice on the screen)
25541 var nopad = nopadtext;
25542 if (lastnode == 'SPAN') {
25546 if (currentElementChild.nodeName == '#text') {
25547 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25548 toadd = nopadtext ? toadd : toadd.trim();
25549 if (!nopad && toadd.length > 80) {
25550 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25552 innerHTML += toadd;
25555 currentElementChild = currentElement.childNodes.item(i);
25561 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25563 // Recursively traverse the tree structure of the child node
25564 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25565 lastnode = currentElementChild.nodeName;
25567 currentElementChild=currentElement.childNodes.item(i);
25573 // The remaining code is mostly for formatting the tree
25574 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25579 ret+= "</"+tagName+">";
25585 applyBlacklists : function()
25587 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25588 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25592 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25593 if (b.indexOf(tag) > -1) {
25596 this.white.push(tag);
25600 Roo.each(w, function(tag) {
25601 if (b.indexOf(tag) > -1) {
25604 if (this.white.indexOf(tag) > -1) {
25607 this.white.push(tag);
25612 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25613 if (w.indexOf(tag) > -1) {
25616 this.black.push(tag);
25620 Roo.each(b, function(tag) {
25621 if (w.indexOf(tag) > -1) {
25624 if (this.black.indexOf(tag) > -1) {
25627 this.black.push(tag);
25632 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25633 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25637 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25638 if (b.indexOf(tag) > -1) {
25641 this.cwhite.push(tag);
25645 Roo.each(w, function(tag) {
25646 if (b.indexOf(tag) > -1) {
25649 if (this.cwhite.indexOf(tag) > -1) {
25652 this.cwhite.push(tag);
25657 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25658 if (w.indexOf(tag) > -1) {
25661 this.cblack.push(tag);
25665 Roo.each(b, function(tag) {
25666 if (w.indexOf(tag) > -1) {
25669 if (this.cblack.indexOf(tag) > -1) {
25672 this.cblack.push(tag);
25677 setStylesheets : function(stylesheets)
25679 if(typeof(stylesheets) == 'string'){
25680 Roo.get(this.iframe.contentDocument.head).createChild({
25682 rel : 'stylesheet',
25691 Roo.each(stylesheets, function(s) {
25696 Roo.get(_this.iframe.contentDocument.head).createChild({
25698 rel : 'stylesheet',
25707 removeStylesheets : function()
25711 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25716 setStyle : function(style)
25718 Roo.get(this.iframe.contentDocument.head).createChild({
25727 // hide stuff that is not compatible
25741 * @event specialkey
25745 * @cfg {String} fieldClass @hide
25748 * @cfg {String} focusClass @hide
25751 * @cfg {String} autoCreate @hide
25754 * @cfg {String} inputType @hide
25757 * @cfg {String} invalidClass @hide
25760 * @cfg {String} invalidText @hide
25763 * @cfg {String} msgFx @hide
25766 * @cfg {String} validateOnBlur @hide
25770 Roo.HtmlEditorCore.white = [
25771 'area', 'br', 'img', 'input', 'hr', 'wbr',
25773 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25774 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25775 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25776 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25777 'table', 'ul', 'xmp',
25779 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25782 'dir', 'menu', 'ol', 'ul', 'dl',
25788 Roo.HtmlEditorCore.black = [
25789 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25791 'base', 'basefont', 'bgsound', 'blink', 'body',
25792 'frame', 'frameset', 'head', 'html', 'ilayer',
25793 'iframe', 'layer', 'link', 'meta', 'object',
25794 'script', 'style' ,'title', 'xml' // clean later..
25796 Roo.HtmlEditorCore.clean = [
25797 'script', 'style', 'title', 'xml'
25799 Roo.HtmlEditorCore.remove = [
25804 Roo.HtmlEditorCore.ablack = [
25808 Roo.HtmlEditorCore.aclean = [
25809 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25813 Roo.HtmlEditorCore.pwhite= [
25814 'http', 'https', 'mailto'
25817 // white listed style attributes.
25818 Roo.HtmlEditorCore.cwhite= [
25819 // 'text-align', /// default is to allow most things..
25825 // black listed style attributes.
25826 Roo.HtmlEditorCore.cblack= [
25827 // 'font-size' -- this can be set by the project
25831 Roo.HtmlEditorCore.swapCodes =[
25832 [ 8211, "–" ],
25833 [ 8212, "—" ],
25850 * @class Roo.bootstrap.HtmlEditor
25851 * @extends Roo.bootstrap.TextArea
25852 * Bootstrap HtmlEditor class
25855 * Create a new HtmlEditor
25856 * @param {Object} config The config object
25859 Roo.bootstrap.HtmlEditor = function(config){
25860 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25861 if (!this.toolbars) {
25862 this.toolbars = [];
25865 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25868 * @event initialize
25869 * Fires when the editor is fully initialized (including the iframe)
25870 * @param {HtmlEditor} this
25875 * Fires when the editor is first receives the focus. Any insertion must wait
25876 * until after this event.
25877 * @param {HtmlEditor} this
25881 * @event beforesync
25882 * Fires before the textarea is updated with content from the editor iframe. Return false
25883 * to cancel the sync.
25884 * @param {HtmlEditor} this
25885 * @param {String} html
25889 * @event beforepush
25890 * Fires before the iframe editor is updated with content from the textarea. Return false
25891 * to cancel the push.
25892 * @param {HtmlEditor} this
25893 * @param {String} html
25898 * Fires when the textarea is updated with content from the editor iframe.
25899 * @param {HtmlEditor} this
25900 * @param {String} html
25905 * Fires when the iframe editor is updated with content from the textarea.
25906 * @param {HtmlEditor} this
25907 * @param {String} html
25911 * @event editmodechange
25912 * Fires when the editor switches edit modes
25913 * @param {HtmlEditor} this
25914 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25916 editmodechange: true,
25918 * @event editorevent
25919 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25920 * @param {HtmlEditor} this
25924 * @event firstfocus
25925 * Fires when on first focus - needed by toolbars..
25926 * @param {HtmlEditor} this
25931 * Auto save the htmlEditor value as a file into Events
25932 * @param {HtmlEditor} this
25936 * @event savedpreview
25937 * preview the saved version of htmlEditor
25938 * @param {HtmlEditor} this
25945 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25949 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25954 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25959 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25964 * @cfg {Number} height (in pixels)
25968 * @cfg {Number} width (in pixels)
25973 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25976 stylesheets: false,
25981 // private properties
25982 validationEvent : false,
25984 initialized : false,
25987 onFocus : Roo.emptyFn,
25989 hideMode:'offsets',
25991 tbContainer : false,
25995 toolbarContainer :function() {
25996 return this.wrap.select('.x-html-editor-tb',true).first();
26000 * Protected method that will not generally be called directly. It
26001 * is called when the editor creates its toolbar. Override this method if you need to
26002 * add custom toolbar buttons.
26003 * @param {HtmlEditor} editor
26005 createToolbar : function(){
26006 Roo.log('renewing');
26007 Roo.log("create toolbars");
26009 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26010 this.toolbars[0].render(this.toolbarContainer());
26014 // if (!editor.toolbars || !editor.toolbars.length) {
26015 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26018 // for (var i =0 ; i < editor.toolbars.length;i++) {
26019 // editor.toolbars[i] = Roo.factory(
26020 // typeof(editor.toolbars[i]) == 'string' ?
26021 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26022 // Roo.bootstrap.HtmlEditor);
26023 // editor.toolbars[i].init(editor);
26029 onRender : function(ct, position)
26031 // Roo.log("Call onRender: " + this.xtype);
26033 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26035 this.wrap = this.inputEl().wrap({
26036 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26039 this.editorcore.onRender(ct, position);
26041 if (this.resizable) {
26042 this.resizeEl = new Roo.Resizable(this.wrap, {
26046 minHeight : this.height,
26047 height: this.height,
26048 handles : this.resizable,
26051 resize : function(r, w, h) {
26052 _t.onResize(w,h); // -something
26058 this.createToolbar(this);
26061 if(!this.width && this.resizable){
26062 this.setSize(this.wrap.getSize());
26064 if (this.resizeEl) {
26065 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26066 // should trigger onReize..
26072 onResize : function(w, h)
26074 Roo.log('resize: ' +w + ',' + h );
26075 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26079 if(this.inputEl() ){
26080 if(typeof w == 'number'){
26081 var aw = w - this.wrap.getFrameWidth('lr');
26082 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26085 if(typeof h == 'number'){
26086 var tbh = -11; // fixme it needs to tool bar size!
26087 for (var i =0; i < this.toolbars.length;i++) {
26088 // fixme - ask toolbars for heights?
26089 tbh += this.toolbars[i].el.getHeight();
26090 //if (this.toolbars[i].footer) {
26091 // tbh += this.toolbars[i].footer.el.getHeight();
26099 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26100 ah -= 5; // knock a few pixes off for look..
26101 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26105 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26106 this.editorcore.onResize(ew,eh);
26111 * Toggles the editor between standard and source edit mode.
26112 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26114 toggleSourceEdit : function(sourceEditMode)
26116 this.editorcore.toggleSourceEdit(sourceEditMode);
26118 if(this.editorcore.sourceEditMode){
26119 Roo.log('editor - showing textarea');
26122 // Roo.log(this.syncValue());
26124 this.inputEl().removeClass(['hide', 'x-hidden']);
26125 this.inputEl().dom.removeAttribute('tabIndex');
26126 this.inputEl().focus();
26128 Roo.log('editor - hiding textarea');
26130 // Roo.log(this.pushValue());
26133 this.inputEl().addClass(['hide', 'x-hidden']);
26134 this.inputEl().dom.setAttribute('tabIndex', -1);
26135 //this.deferFocus();
26138 if(this.resizable){
26139 this.setSize(this.wrap.getSize());
26142 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26145 // private (for BoxComponent)
26146 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26148 // private (for BoxComponent)
26149 getResizeEl : function(){
26153 // private (for BoxComponent)
26154 getPositionEl : function(){
26159 initEvents : function(){
26160 this.originalValue = this.getValue();
26164 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26167 // markInvalid : Roo.emptyFn,
26169 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26172 // clearInvalid : Roo.emptyFn,
26174 setValue : function(v){
26175 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26176 this.editorcore.pushValue();
26181 deferFocus : function(){
26182 this.focus.defer(10, this);
26186 focus : function(){
26187 this.editorcore.focus();
26193 onDestroy : function(){
26199 for (var i =0; i < this.toolbars.length;i++) {
26200 // fixme - ask toolbars for heights?
26201 this.toolbars[i].onDestroy();
26204 this.wrap.dom.innerHTML = '';
26205 this.wrap.remove();
26210 onFirstFocus : function(){
26211 //Roo.log("onFirstFocus");
26212 this.editorcore.onFirstFocus();
26213 for (var i =0; i < this.toolbars.length;i++) {
26214 this.toolbars[i].onFirstFocus();
26220 syncValue : function()
26222 this.editorcore.syncValue();
26225 pushValue : function()
26227 this.editorcore.pushValue();
26231 // hide stuff that is not compatible
26245 * @event specialkey
26249 * @cfg {String} fieldClass @hide
26252 * @cfg {String} focusClass @hide
26255 * @cfg {String} autoCreate @hide
26258 * @cfg {String} inputType @hide
26262 * @cfg {String} invalidText @hide
26265 * @cfg {String} msgFx @hide
26268 * @cfg {String} validateOnBlur @hide
26277 Roo.namespace('Roo.bootstrap.htmleditor');
26279 * @class Roo.bootstrap.HtmlEditorToolbar1
26285 new Roo.bootstrap.HtmlEditor({
26288 new Roo.bootstrap.HtmlEditorToolbar1({
26289 disable : { fonts: 1 , format: 1, ..., ... , ...],
26295 * @cfg {Object} disable List of elements to disable..
26296 * @cfg {Array} btns List of additional buttons.
26300 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26303 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26306 Roo.apply(this, config);
26308 // default disabled, based on 'good practice'..
26309 this.disable = this.disable || {};
26310 Roo.applyIf(this.disable, {
26313 specialElements : true
26315 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26317 this.editor = config.editor;
26318 this.editorcore = config.editor.editorcore;
26320 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26322 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26323 // dont call parent... till later.
26325 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26330 editorcore : false,
26335 "h1","h2","h3","h4","h5","h6",
26337 "abbr", "acronym", "address", "cite", "samp", "var",
26341 onRender : function(ct, position)
26343 // Roo.log("Call onRender: " + this.xtype);
26345 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26347 this.el.dom.style.marginBottom = '0';
26349 var editorcore = this.editorcore;
26350 var editor= this.editor;
26353 var btn = function(id,cmd , toggle, handler, html){
26355 var event = toggle ? 'toggle' : 'click';
26360 xns: Roo.bootstrap,
26364 enableToggle:toggle !== false,
26366 pressed : toggle ? false : null,
26369 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26370 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26376 // var cb_box = function...
26381 xns: Roo.bootstrap,
26386 xns: Roo.bootstrap,
26390 Roo.each(this.formats, function(f) {
26391 style.menu.items.push({
26393 xns: Roo.bootstrap,
26394 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26399 editorcore.insertTag(this.tagname);
26406 children.push(style);
26408 btn('bold',false,true);
26409 btn('italic',false,true);
26410 btn('align-left', 'justifyleft',true);
26411 btn('align-center', 'justifycenter',true);
26412 btn('align-right' , 'justifyright',true);
26413 btn('link', false, false, function(btn) {
26414 //Roo.log("create link?");
26415 var url = prompt(this.createLinkText, this.defaultLinkValue);
26416 if(url && url != 'http:/'+'/'){
26417 this.editorcore.relayCmd('createlink', url);
26420 btn('list','insertunorderedlist',true);
26421 btn('pencil', false,true, function(btn){
26423 this.toggleSourceEdit(btn.pressed);
26426 if (this.editor.btns.length > 0) {
26427 for (var i = 0; i<this.editor.btns.length; i++) {
26428 children.push(this.editor.btns[i]);
26436 xns: Roo.bootstrap,
26441 xns: Roo.bootstrap,
26446 cog.menu.items.push({
26448 xns: Roo.bootstrap,
26449 html : Clean styles,
26454 editorcore.insertTag(this.tagname);
26463 this.xtype = 'NavSimplebar';
26465 for(var i=0;i< children.length;i++) {
26467 this.buttons.add(this.addxtypeChild(children[i]));
26471 editor.on('editorevent', this.updateToolbar, this);
26473 onBtnClick : function(id)
26475 this.editorcore.relayCmd(id);
26476 this.editorcore.focus();
26480 * Protected method that will not generally be called directly. It triggers
26481 * a toolbar update by reading the markup state of the current selection in the editor.
26483 updateToolbar: function(){
26485 if(!this.editorcore.activated){
26486 this.editor.onFirstFocus(); // is this neeed?
26490 var btns = this.buttons;
26491 var doc = this.editorcore.doc;
26492 btns.get('bold').setActive(doc.queryCommandState('bold'));
26493 btns.get('italic').setActive(doc.queryCommandState('italic'));
26494 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26496 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26497 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26498 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26500 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26501 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26504 var ans = this.editorcore.getAllAncestors();
26505 if (this.formatCombo) {
26508 var store = this.formatCombo.store;
26509 this.formatCombo.setValue("");
26510 for (var i =0; i < ans.length;i++) {
26511 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26513 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26521 // hides menus... - so this cant be on a menu...
26522 Roo.bootstrap.MenuMgr.hideAll();
26524 Roo.bootstrap.MenuMgr.hideAll();
26525 //this.editorsyncValue();
26527 onFirstFocus: function() {
26528 this.buttons.each(function(item){
26532 toggleSourceEdit : function(sourceEditMode){
26535 if(sourceEditMode){
26536 Roo.log("disabling buttons");
26537 this.buttons.each( function(item){
26538 if(item.cmd != 'pencil'){
26544 Roo.log("enabling buttons");
26545 if(this.editorcore.initialized){
26546 this.buttons.each( function(item){
26552 Roo.log("calling toggole on editor");
26553 // tell the editor that it's been pressed..
26554 this.editor.toggleSourceEdit(sourceEditMode);
26568 * @class Roo.bootstrap.Markdown
26569 * @extends Roo.bootstrap.TextArea
26570 * Bootstrap Showdown editable area
26571 * @cfg {string} content
26574 * Create a new Showdown
26577 Roo.bootstrap.Markdown = function(config){
26578 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26582 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26586 initEvents : function()
26589 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26590 this.markdownEl = this.el.createChild({
26591 cls : 'roo-markdown-area'
26593 this.inputEl().addClass('d-none');
26594 if (this.getValue() == '') {
26595 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26598 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26600 this.markdownEl.on('click', this.toggleTextEdit, this);
26601 this.on('blur', this.toggleTextEdit, this);
26602 this.on('specialkey', this.resizeTextArea, this);
26605 toggleTextEdit : function()
26607 var sh = this.markdownEl.getHeight();
26608 this.inputEl().addClass('d-none');
26609 this.markdownEl.addClass('d-none');
26610 if (!this.editing) {
26612 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26613 this.inputEl().removeClass('d-none');
26614 this.inputEl().focus();
26615 this.editing = true;
26618 // show showdown...
26619 this.updateMarkdown();
26620 this.markdownEl.removeClass('d-none');
26621 this.editing = false;
26624 updateMarkdown : function()
26626 if (this.getValue() == '') {
26627 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26631 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26634 resizeTextArea: function () {
26637 Roo.log([sh, this.getValue().split("\n").length * 30]);
26638 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26640 setValue : function(val)
26642 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26643 if (!this.editing) {
26644 this.updateMarkdown();
26650 if (!this.editing) {
26651 this.toggleTextEdit();
26659 * @class Roo.bootstrap.Table.AbstractSelectionModel
26660 * @extends Roo.util.Observable
26661 * Abstract base class for grid SelectionModels. It provides the interface that should be
26662 * implemented by descendant classes. This class should not be directly instantiated.
26665 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26666 this.locked = false;
26667 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26671 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26672 /** @ignore Called by the grid automatically. Do not call directly. */
26673 init : function(grid){
26679 * Locks the selections.
26682 this.locked = true;
26686 * Unlocks the selections.
26688 unlock : function(){
26689 this.locked = false;
26693 * Returns true if the selections are locked.
26694 * @return {Boolean}
26696 isLocked : function(){
26697 return this.locked;
26701 initEvents : function ()
26707 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26708 * @class Roo.bootstrap.Table.RowSelectionModel
26709 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26710 * It supports multiple selections and keyboard selection/navigation.
26712 * @param {Object} config
26715 Roo.bootstrap.Table.RowSelectionModel = function(config){
26716 Roo.apply(this, config);
26717 this.selections = new Roo.util.MixedCollection(false, function(o){
26722 this.lastActive = false;
26726 * @event selectionchange
26727 * Fires when the selection changes
26728 * @param {SelectionModel} this
26730 "selectionchange" : true,
26732 * @event afterselectionchange
26733 * Fires after the selection changes (eg. by key press or clicking)
26734 * @param {SelectionModel} this
26736 "afterselectionchange" : true,
26738 * @event beforerowselect
26739 * Fires when a row is selected being selected, return false to cancel.
26740 * @param {SelectionModel} this
26741 * @param {Number} rowIndex The selected index
26742 * @param {Boolean} keepExisting False if other selections will be cleared
26744 "beforerowselect" : true,
26747 * Fires when a row is selected.
26748 * @param {SelectionModel} this
26749 * @param {Number} rowIndex The selected index
26750 * @param {Roo.data.Record} r The record
26752 "rowselect" : true,
26754 * @event rowdeselect
26755 * Fires when a row is deselected.
26756 * @param {SelectionModel} this
26757 * @param {Number} rowIndex The selected index
26759 "rowdeselect" : true
26761 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26762 this.locked = false;
26765 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26767 * @cfg {Boolean} singleSelect
26768 * True to allow selection of only one row at a time (defaults to false)
26770 singleSelect : false,
26773 initEvents : function()
26776 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26777 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26778 //}else{ // allow click to work like normal
26779 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26781 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26782 this.grid.on("rowclick", this.handleMouseDown, this);
26784 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26785 "up" : function(e){
26787 this.selectPrevious(e.shiftKey);
26788 }else if(this.last !== false && this.lastActive !== false){
26789 var last = this.last;
26790 this.selectRange(this.last, this.lastActive-1);
26791 this.grid.getView().focusRow(this.lastActive);
26792 if(last !== false){
26796 this.selectFirstRow();
26798 this.fireEvent("afterselectionchange", this);
26800 "down" : function(e){
26802 this.selectNext(e.shiftKey);
26803 }else if(this.last !== false && this.lastActive !== false){
26804 var last = this.last;
26805 this.selectRange(this.last, this.lastActive+1);
26806 this.grid.getView().focusRow(this.lastActive);
26807 if(last !== false){
26811 this.selectFirstRow();
26813 this.fireEvent("afterselectionchange", this);
26817 this.grid.store.on('load', function(){
26818 this.selections.clear();
26821 var view = this.grid.view;
26822 view.on("refresh", this.onRefresh, this);
26823 view.on("rowupdated", this.onRowUpdated, this);
26824 view.on("rowremoved", this.onRemove, this);
26829 onRefresh : function()
26831 var ds = this.grid.store, i, v = this.grid.view;
26832 var s = this.selections;
26833 s.each(function(r){
26834 if((i = ds.indexOfId(r.id)) != -1){
26843 onRemove : function(v, index, r){
26844 this.selections.remove(r);
26848 onRowUpdated : function(v, index, r){
26849 if(this.isSelected(r)){
26850 v.onRowSelect(index);
26856 * @param {Array} records The records to select
26857 * @param {Boolean} keepExisting (optional) True to keep existing selections
26859 selectRecords : function(records, keepExisting)
26862 this.clearSelections();
26864 var ds = this.grid.store;
26865 for(var i = 0, len = records.length; i < len; i++){
26866 this.selectRow(ds.indexOf(records[i]), true);
26871 * Gets the number of selected rows.
26874 getCount : function(){
26875 return this.selections.length;
26879 * Selects the first row in the grid.
26881 selectFirstRow : function(){
26886 * Select the last row.
26887 * @param {Boolean} keepExisting (optional) True to keep existing selections
26889 selectLastRow : function(keepExisting){
26890 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26891 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26895 * Selects the row immediately following the last selected row.
26896 * @param {Boolean} keepExisting (optional) True to keep existing selections
26898 selectNext : function(keepExisting)
26900 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26901 this.selectRow(this.last+1, keepExisting);
26902 this.grid.getView().focusRow(this.last);
26907 * Selects the row that precedes the last selected row.
26908 * @param {Boolean} keepExisting (optional) True to keep existing selections
26910 selectPrevious : function(keepExisting){
26912 this.selectRow(this.last-1, keepExisting);
26913 this.grid.getView().focusRow(this.last);
26918 * Returns the selected records
26919 * @return {Array} Array of selected records
26921 getSelections : function(){
26922 return [].concat(this.selections.items);
26926 * Returns the first selected record.
26929 getSelected : function(){
26930 return this.selections.itemAt(0);
26935 * Clears all selections.
26937 clearSelections : function(fast)
26943 var ds = this.grid.store;
26944 var s = this.selections;
26945 s.each(function(r){
26946 this.deselectRow(ds.indexOfId(r.id));
26950 this.selections.clear();
26957 * Selects all rows.
26959 selectAll : function(){
26963 this.selections.clear();
26964 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26965 this.selectRow(i, true);
26970 * Returns True if there is a selection.
26971 * @return {Boolean}
26973 hasSelection : function(){
26974 return this.selections.length > 0;
26978 * Returns True if the specified row is selected.
26979 * @param {Number/Record} record The record or index of the record to check
26980 * @return {Boolean}
26982 isSelected : function(index){
26983 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26984 return (r && this.selections.key(r.id) ? true : false);
26988 * Returns True if the specified record id is selected.
26989 * @param {String} id The id of record to check
26990 * @return {Boolean}
26992 isIdSelected : function(id){
26993 return (this.selections.key(id) ? true : false);
26998 handleMouseDBClick : function(e, t){
27002 handleMouseDown : function(e, t)
27004 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27005 if(this.isLocked() || rowIndex < 0 ){
27008 if(e.shiftKey && this.last !== false){
27009 var last = this.last;
27010 this.selectRange(last, rowIndex, e.ctrlKey);
27011 this.last = last; // reset the last
27015 var isSelected = this.isSelected(rowIndex);
27016 //Roo.log("select row:" + rowIndex);
27018 this.deselectRow(rowIndex);
27020 this.selectRow(rowIndex, true);
27024 if(e.button !== 0 && isSelected){
27025 alert('rowIndex 2: ' + rowIndex);
27026 view.focusRow(rowIndex);
27027 }else if(e.ctrlKey && isSelected){
27028 this.deselectRow(rowIndex);
27029 }else if(!isSelected){
27030 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27031 view.focusRow(rowIndex);
27035 this.fireEvent("afterselectionchange", this);
27038 handleDragableRowClick : function(grid, rowIndex, e)
27040 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27041 this.selectRow(rowIndex, false);
27042 grid.view.focusRow(rowIndex);
27043 this.fireEvent("afterselectionchange", this);
27048 * Selects multiple rows.
27049 * @param {Array} rows Array of the indexes of the row to select
27050 * @param {Boolean} keepExisting (optional) True to keep existing selections
27052 selectRows : function(rows, keepExisting){
27054 this.clearSelections();
27056 for(var i = 0, len = rows.length; i < len; i++){
27057 this.selectRow(rows[i], true);
27062 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27063 * @param {Number} startRow The index of the first row in the range
27064 * @param {Number} endRow The index of the last row in the range
27065 * @param {Boolean} keepExisting (optional) True to retain existing selections
27067 selectRange : function(startRow, endRow, keepExisting){
27072 this.clearSelections();
27074 if(startRow <= endRow){
27075 for(var i = startRow; i <= endRow; i++){
27076 this.selectRow(i, true);
27079 for(var i = startRow; i >= endRow; i--){
27080 this.selectRow(i, true);
27086 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27087 * @param {Number} startRow The index of the first row in the range
27088 * @param {Number} endRow The index of the last row in the range
27090 deselectRange : function(startRow, endRow, preventViewNotify){
27094 for(var i = startRow; i <= endRow; i++){
27095 this.deselectRow(i, preventViewNotify);
27101 * @param {Number} row The index of the row to select
27102 * @param {Boolean} keepExisting (optional) True to keep existing selections
27104 selectRow : function(index, keepExisting, preventViewNotify)
27106 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27109 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27110 if(!keepExisting || this.singleSelect){
27111 this.clearSelections();
27114 var r = this.grid.store.getAt(index);
27115 //console.log('selectRow - record id :' + r.id);
27117 this.selections.add(r);
27118 this.last = this.lastActive = index;
27119 if(!preventViewNotify){
27120 var proxy = new Roo.Element(
27121 this.grid.getRowDom(index)
27123 proxy.addClass('bg-info info');
27125 this.fireEvent("rowselect", this, index, r);
27126 this.fireEvent("selectionchange", this);
27132 * @param {Number} row The index of the row to deselect
27134 deselectRow : function(index, preventViewNotify)
27139 if(this.last == index){
27142 if(this.lastActive == index){
27143 this.lastActive = false;
27146 var r = this.grid.store.getAt(index);
27151 this.selections.remove(r);
27152 //.console.log('deselectRow - record id :' + r.id);
27153 if(!preventViewNotify){
27155 var proxy = new Roo.Element(
27156 this.grid.getRowDom(index)
27158 proxy.removeClass('bg-info info');
27160 this.fireEvent("rowdeselect", this, index);
27161 this.fireEvent("selectionchange", this);
27165 restoreLast : function(){
27167 this.last = this._last;
27172 acceptsNav : function(row, col, cm){
27173 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27177 onEditorKey : function(field, e){
27178 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27183 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27185 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27187 }else if(k == e.ENTER && !e.ctrlKey){
27191 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27193 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27195 }else if(k == e.ESC){
27199 g.startEditing(newCell[0], newCell[1]);
27205 * Ext JS Library 1.1.1
27206 * Copyright(c) 2006-2007, Ext JS, LLC.
27208 * Originally Released Under LGPL - original licence link has changed is not relivant.
27211 * <script type="text/javascript">
27215 * @class Roo.bootstrap.PagingToolbar
27216 * @extends Roo.bootstrap.NavSimplebar
27217 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27219 * Create a new PagingToolbar
27220 * @param {Object} config The config object
27221 * @param {Roo.data.Store} store
27223 Roo.bootstrap.PagingToolbar = function(config)
27225 // old args format still supported... - xtype is prefered..
27226 // created from xtype...
27228 this.ds = config.dataSource;
27230 if (config.store && !this.ds) {
27231 this.store= Roo.factory(config.store, Roo.data);
27232 this.ds = this.store;
27233 this.ds.xmodule = this.xmodule || false;
27236 this.toolbarItems = [];
27237 if (config.items) {
27238 this.toolbarItems = config.items;
27241 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27246 this.bind(this.ds);
27249 if (Roo.bootstrap.version == 4) {
27250 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27252 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27257 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27259 * @cfg {Roo.data.Store} dataSource
27260 * The underlying data store providing the paged data
27263 * @cfg {String/HTMLElement/Element} container
27264 * container The id or element that will contain the toolbar
27267 * @cfg {Boolean} displayInfo
27268 * True to display the displayMsg (defaults to false)
27271 * @cfg {Number} pageSize
27272 * The number of records to display per page (defaults to 20)
27276 * @cfg {String} displayMsg
27277 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27279 displayMsg : 'Displaying {0} - {1} of {2}',
27281 * @cfg {String} emptyMsg
27282 * The message to display when no records are found (defaults to "No data to display")
27284 emptyMsg : 'No data to display',
27286 * Customizable piece of the default paging text (defaults to "Page")
27289 beforePageText : "Page",
27291 * Customizable piece of the default paging text (defaults to "of %0")
27294 afterPageText : "of {0}",
27296 * Customizable piece of the default paging text (defaults to "First Page")
27299 firstText : "First Page",
27301 * Customizable piece of the default paging text (defaults to "Previous Page")
27304 prevText : "Previous Page",
27306 * Customizable piece of the default paging text (defaults to "Next Page")
27309 nextText : "Next Page",
27311 * Customizable piece of the default paging text (defaults to "Last Page")
27314 lastText : "Last Page",
27316 * Customizable piece of the default paging text (defaults to "Refresh")
27319 refreshText : "Refresh",
27323 onRender : function(ct, position)
27325 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27326 this.navgroup.parentId = this.id;
27327 this.navgroup.onRender(this.el, null);
27328 // add the buttons to the navgroup
27330 if(this.displayInfo){
27331 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27332 this.displayEl = this.el.select('.x-paging-info', true).first();
27333 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27334 // this.displayEl = navel.el.select('span',true).first();
27340 Roo.each(_this.buttons, function(e){ // this might need to use render????
27341 Roo.factory(e).render(_this.el);
27345 Roo.each(_this.toolbarItems, function(e) {
27346 _this.navgroup.addItem(e);
27350 this.first = this.navgroup.addItem({
27351 tooltip: this.firstText,
27352 cls: "prev btn-outline-secondary",
27353 html : ' <i class="fa fa-step-backward"></i>',
27355 preventDefault: true,
27356 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27359 this.prev = this.navgroup.addItem({
27360 tooltip: this.prevText,
27361 cls: "prev btn-outline-secondary",
27362 html : ' <i class="fa fa-backward"></i>',
27364 preventDefault: true,
27365 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27367 //this.addSeparator();
27370 var field = this.navgroup.addItem( {
27372 cls : 'x-paging-position btn-outline-secondary',
27374 html : this.beforePageText +
27375 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27376 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27379 this.field = field.el.select('input', true).first();
27380 this.field.on("keydown", this.onPagingKeydown, this);
27381 this.field.on("focus", function(){this.dom.select();});
27384 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27385 //this.field.setHeight(18);
27386 //this.addSeparator();
27387 this.next = this.navgroup.addItem({
27388 tooltip: this.nextText,
27389 cls: "next btn-outline-secondary",
27390 html : ' <i class="fa fa-forward"></i>',
27392 preventDefault: true,
27393 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27395 this.last = this.navgroup.addItem({
27396 tooltip: this.lastText,
27397 html : ' <i class="fa fa-step-forward"></i>',
27398 cls: "next btn-outline-secondary",
27400 preventDefault: true,
27401 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27403 //this.addSeparator();
27404 this.loading = this.navgroup.addItem({
27405 tooltip: this.refreshText,
27406 cls: "btn-outline-secondary",
27407 html : ' <i class="fa fa-refresh"></i>',
27408 preventDefault: true,
27409 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27415 updateInfo : function(){
27416 if(this.displayEl){
27417 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27418 var msg = count == 0 ?
27422 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27424 this.displayEl.update(msg);
27429 onLoad : function(ds, r, o)
27431 this.cursor = o.params && o.params.start ? o.params.start : 0;
27433 var d = this.getPageData(),
27438 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27439 this.field.dom.value = ap;
27440 this.first.setDisabled(ap == 1);
27441 this.prev.setDisabled(ap == 1);
27442 this.next.setDisabled(ap == ps);
27443 this.last.setDisabled(ap == ps);
27444 this.loading.enable();
27449 getPageData : function(){
27450 var total = this.ds.getTotalCount();
27453 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27454 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27459 onLoadError : function(){
27460 this.loading.enable();
27464 onPagingKeydown : function(e){
27465 var k = e.getKey();
27466 var d = this.getPageData();
27468 var v = this.field.dom.value, pageNum;
27469 if(!v || isNaN(pageNum = parseInt(v, 10))){
27470 this.field.dom.value = d.activePage;
27473 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27474 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27477 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))
27479 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27480 this.field.dom.value = pageNum;
27481 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27484 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27486 var v = this.field.dom.value, pageNum;
27487 var increment = (e.shiftKey) ? 10 : 1;
27488 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27491 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27492 this.field.dom.value = d.activePage;
27495 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27497 this.field.dom.value = parseInt(v, 10) + increment;
27498 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27499 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27506 beforeLoad : function(){
27508 this.loading.disable();
27513 onClick : function(which){
27522 ds.load({params:{start: 0, limit: this.pageSize}});
27525 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27528 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27531 var total = ds.getTotalCount();
27532 var extra = total % this.pageSize;
27533 var lastStart = extra ? (total - extra) : total-this.pageSize;
27534 ds.load({params:{start: lastStart, limit: this.pageSize}});
27537 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27543 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27544 * @param {Roo.data.Store} store The data store to unbind
27546 unbind : function(ds){
27547 ds.un("beforeload", this.beforeLoad, this);
27548 ds.un("load", this.onLoad, this);
27549 ds.un("loadexception", this.onLoadError, this);
27550 ds.un("remove", this.updateInfo, this);
27551 ds.un("add", this.updateInfo, this);
27552 this.ds = undefined;
27556 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27557 * @param {Roo.data.Store} store The data store to bind
27559 bind : function(ds){
27560 ds.on("beforeload", this.beforeLoad, this);
27561 ds.on("load", this.onLoad, this);
27562 ds.on("loadexception", this.onLoadError, this);
27563 ds.on("remove", this.updateInfo, this);
27564 ds.on("add", this.updateInfo, this);
27575 * @class Roo.bootstrap.MessageBar
27576 * @extends Roo.bootstrap.Component
27577 * Bootstrap MessageBar class
27578 * @cfg {String} html contents of the MessageBar
27579 * @cfg {String} weight (info | success | warning | danger) default info
27580 * @cfg {String} beforeClass insert the bar before the given class
27581 * @cfg {Boolean} closable (true | false) default false
27582 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27585 * Create a new Element
27586 * @param {Object} config The config object
27589 Roo.bootstrap.MessageBar = function(config){
27590 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27593 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27599 beforeClass: 'bootstrap-sticky-wrap',
27601 getAutoCreate : function(){
27605 cls: 'alert alert-dismissable alert-' + this.weight,
27610 html: this.html || ''
27616 cfg.cls += ' alert-messages-fixed';
27630 onRender : function(ct, position)
27632 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27635 var cfg = Roo.apply({}, this.getAutoCreate());
27639 cfg.cls += ' ' + this.cls;
27642 cfg.style = this.style;
27644 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27646 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27649 this.el.select('>button.close').on('click', this.hide, this);
27655 if (!this.rendered) {
27661 this.fireEvent('show', this);
27667 if (!this.rendered) {
27673 this.fireEvent('hide', this);
27676 update : function()
27678 // var e = this.el.dom.firstChild;
27680 // if(this.closable){
27681 // e = e.nextSibling;
27684 // e.data = this.html || '';
27686 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27702 * @class Roo.bootstrap.Graph
27703 * @extends Roo.bootstrap.Component
27704 * Bootstrap Graph class
27708 @cfg {String} graphtype bar | vbar | pie
27709 @cfg {number} g_x coodinator | centre x (pie)
27710 @cfg {number} g_y coodinator | centre y (pie)
27711 @cfg {number} g_r radius (pie)
27712 @cfg {number} g_height height of the chart (respected by all elements in the set)
27713 @cfg {number} g_width width of the chart (respected by all elements in the set)
27714 @cfg {Object} title The title of the chart
27717 -opts (object) options for the chart
27719 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27720 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27722 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.
27723 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27725 o stretch (boolean)
27727 -opts (object) options for the pie
27730 o startAngle (number)
27731 o endAngle (number)
27735 * Create a new Input
27736 * @param {Object} config The config object
27739 Roo.bootstrap.Graph = function(config){
27740 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27746 * The img click event for the img.
27747 * @param {Roo.EventObject} e
27753 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27764 //g_colors: this.colors,
27771 getAutoCreate : function(){
27782 onRender : function(ct,position){
27785 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27787 if (typeof(Raphael) == 'undefined') {
27788 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27792 this.raphael = Raphael(this.el.dom);
27794 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27795 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27796 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27797 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27799 r.text(160, 10, "Single Series Chart").attr(txtattr);
27800 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27801 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27802 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27804 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27805 r.barchart(330, 10, 300, 220, data1);
27806 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27807 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27810 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27811 // r.barchart(30, 30, 560, 250, xdata, {
27812 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27813 // axis : "0 0 1 1",
27814 // axisxlabels : xdata
27815 // //yvalues : cols,
27818 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27820 // this.load(null,xdata,{
27821 // axis : "0 0 1 1",
27822 // axisxlabels : xdata
27827 load : function(graphtype,xdata,opts)
27829 this.raphael.clear();
27831 graphtype = this.graphtype;
27836 var r = this.raphael,
27837 fin = function () {
27838 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27840 fout = function () {
27841 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27843 pfin = function() {
27844 this.sector.stop();
27845 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27848 this.label[0].stop();
27849 this.label[0].attr({ r: 7.5 });
27850 this.label[1].attr({ "font-weight": 800 });
27853 pfout = function() {
27854 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27857 this.label[0].animate({ r: 5 }, 500, "bounce");
27858 this.label[1].attr({ "font-weight": 400 });
27864 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27867 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27870 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27871 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27873 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27880 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27885 setTitle: function(o)
27890 initEvents: function() {
27893 this.el.on('click', this.onClick, this);
27897 onClick : function(e)
27899 Roo.log('img onclick');
27900 this.fireEvent('click', this, e);
27912 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27915 * @class Roo.bootstrap.dash.NumberBox
27916 * @extends Roo.bootstrap.Component
27917 * Bootstrap NumberBox class
27918 * @cfg {String} headline Box headline
27919 * @cfg {String} content Box content
27920 * @cfg {String} icon Box icon
27921 * @cfg {String} footer Footer text
27922 * @cfg {String} fhref Footer href
27925 * Create a new NumberBox
27926 * @param {Object} config The config object
27930 Roo.bootstrap.dash.NumberBox = function(config){
27931 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27935 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27944 getAutoCreate : function(){
27948 cls : 'small-box ',
27956 cls : 'roo-headline',
27957 html : this.headline
27961 cls : 'roo-content',
27962 html : this.content
27976 cls : 'ion ' + this.icon
27985 cls : 'small-box-footer',
27986 href : this.fhref || '#',
27990 cfg.cn.push(footer);
27997 onRender : function(ct,position){
27998 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28005 setHeadline: function (value)
28007 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28010 setFooter: function (value, href)
28012 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28015 this.el.select('a.small-box-footer',true).first().attr('href', href);
28020 setContent: function (value)
28022 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28025 initEvents: function()
28039 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28042 * @class Roo.bootstrap.dash.TabBox
28043 * @extends Roo.bootstrap.Component
28044 * Bootstrap TabBox class
28045 * @cfg {String} title Title of the TabBox
28046 * @cfg {String} icon Icon of the TabBox
28047 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28048 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28051 * Create a new TabBox
28052 * @param {Object} config The config object
28056 Roo.bootstrap.dash.TabBox = function(config){
28057 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28062 * When a pane is added
28063 * @param {Roo.bootstrap.dash.TabPane} pane
28067 * @event activatepane
28068 * When a pane is activated
28069 * @param {Roo.bootstrap.dash.TabPane} pane
28071 "activatepane" : true
28079 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28084 tabScrollable : false,
28086 getChildContainer : function()
28088 return this.el.select('.tab-content', true).first();
28091 getAutoCreate : function(){
28095 cls: 'pull-left header',
28103 cls: 'fa ' + this.icon
28109 cls: 'nav nav-tabs pull-right',
28115 if(this.tabScrollable){
28122 cls: 'nav nav-tabs pull-right',
28133 cls: 'nav-tabs-custom',
28138 cls: 'tab-content no-padding',
28146 initEvents : function()
28148 //Roo.log('add add pane handler');
28149 this.on('addpane', this.onAddPane, this);
28152 * Updates the box title
28153 * @param {String} html to set the title to.
28155 setTitle : function(value)
28157 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28159 onAddPane : function(pane)
28161 this.panes.push(pane);
28162 //Roo.log('addpane');
28164 // tabs are rendere left to right..
28165 if(!this.showtabs){
28169 var ctr = this.el.select('.nav-tabs', true).first();
28172 var existing = ctr.select('.nav-tab',true);
28173 var qty = existing.getCount();;
28176 var tab = ctr.createChild({
28178 cls : 'nav-tab' + (qty ? '' : ' active'),
28186 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28189 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28191 pane.el.addClass('active');
28196 onTabClick : function(ev,un,ob,pane)
28198 //Roo.log('tab - prev default');
28199 ev.preventDefault();
28202 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28203 pane.tab.addClass('active');
28204 //Roo.log(pane.title);
28205 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28206 // technically we should have a deactivate event.. but maybe add later.
28207 // and it should not de-activate the selected tab...
28208 this.fireEvent('activatepane', pane);
28209 pane.el.addClass('active');
28210 pane.fireEvent('activate');
28215 getActivePane : function()
28218 Roo.each(this.panes, function(p) {
28219 if(p.el.hasClass('active')){
28240 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28242 * @class Roo.bootstrap.TabPane
28243 * @extends Roo.bootstrap.Component
28244 * Bootstrap TabPane class
28245 * @cfg {Boolean} active (false | true) Default false
28246 * @cfg {String} title title of panel
28250 * Create a new TabPane
28251 * @param {Object} config The config object
28254 Roo.bootstrap.dash.TabPane = function(config){
28255 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28261 * When a pane is activated
28262 * @param {Roo.bootstrap.dash.TabPane} pane
28269 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28274 // the tabBox that this is attached to.
28277 getAutoCreate : function()
28285 cfg.cls += ' active';
28290 initEvents : function()
28292 //Roo.log('trigger add pane handler');
28293 this.parent().fireEvent('addpane', this)
28297 * Updates the tab title
28298 * @param {String} html to set the title to.
28300 setTitle: function(str)
28306 this.tab.select('a', true).first().dom.innerHTML = str;
28323 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28326 * @class Roo.bootstrap.menu.Menu
28327 * @extends Roo.bootstrap.Component
28328 * Bootstrap Menu class - container for Menu
28329 * @cfg {String} html Text of the menu
28330 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28331 * @cfg {String} icon Font awesome icon
28332 * @cfg {String} pos Menu align to (top | bottom) default bottom
28336 * Create a new Menu
28337 * @param {Object} config The config object
28341 Roo.bootstrap.menu.Menu = function(config){
28342 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28346 * @event beforeshow
28347 * Fires before this menu is displayed
28348 * @param {Roo.bootstrap.menu.Menu} this
28352 * @event beforehide
28353 * Fires before this menu is hidden
28354 * @param {Roo.bootstrap.menu.Menu} this
28359 * Fires after this menu is displayed
28360 * @param {Roo.bootstrap.menu.Menu} this
28365 * Fires after this menu is hidden
28366 * @param {Roo.bootstrap.menu.Menu} this
28371 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28372 * @param {Roo.bootstrap.menu.Menu} this
28373 * @param {Roo.EventObject} e
28380 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28384 weight : 'default',
28389 getChildContainer : function() {
28390 if(this.isSubMenu){
28394 return this.el.select('ul.dropdown-menu', true).first();
28397 getAutoCreate : function()
28402 cls : 'roo-menu-text',
28410 cls : 'fa ' + this.icon
28421 cls : 'dropdown-button btn btn-' + this.weight,
28426 cls : 'dropdown-toggle btn btn-' + this.weight,
28436 cls : 'dropdown-menu'
28442 if(this.pos == 'top'){
28443 cfg.cls += ' dropup';
28446 if(this.isSubMenu){
28449 cls : 'dropdown-menu'
28456 onRender : function(ct, position)
28458 this.isSubMenu = ct.hasClass('dropdown-submenu');
28460 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28463 initEvents : function()
28465 if(this.isSubMenu){
28469 this.hidden = true;
28471 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28472 this.triggerEl.on('click', this.onTriggerPress, this);
28474 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28475 this.buttonEl.on('click', this.onClick, this);
28481 if(this.isSubMenu){
28485 return this.el.select('ul.dropdown-menu', true).first();
28488 onClick : function(e)
28490 this.fireEvent("click", this, e);
28493 onTriggerPress : function(e)
28495 if (this.isVisible()) {
28502 isVisible : function(){
28503 return !this.hidden;
28508 this.fireEvent("beforeshow", this);
28510 this.hidden = false;
28511 this.el.addClass('open');
28513 Roo.get(document).on("mouseup", this.onMouseUp, this);
28515 this.fireEvent("show", this);
28522 this.fireEvent("beforehide", this);
28524 this.hidden = true;
28525 this.el.removeClass('open');
28527 Roo.get(document).un("mouseup", this.onMouseUp);
28529 this.fireEvent("hide", this);
28532 onMouseUp : function()
28546 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28549 * @class Roo.bootstrap.menu.Item
28550 * @extends Roo.bootstrap.Component
28551 * Bootstrap MenuItem class
28552 * @cfg {Boolean} submenu (true | false) default false
28553 * @cfg {String} html text of the item
28554 * @cfg {String} href the link
28555 * @cfg {Boolean} disable (true | false) default false
28556 * @cfg {Boolean} preventDefault (true | false) default true
28557 * @cfg {String} icon Font awesome icon
28558 * @cfg {String} pos Submenu align to (left | right) default right
28562 * Create a new Item
28563 * @param {Object} config The config object
28567 Roo.bootstrap.menu.Item = function(config){
28568 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28572 * Fires when the mouse is hovering over this menu
28573 * @param {Roo.bootstrap.menu.Item} this
28574 * @param {Roo.EventObject} e
28579 * Fires when the mouse exits this menu
28580 * @param {Roo.bootstrap.menu.Item} this
28581 * @param {Roo.EventObject} e
28587 * The raw click event for the entire grid.
28588 * @param {Roo.EventObject} e
28594 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28599 preventDefault: true,
28604 getAutoCreate : function()
28609 cls : 'roo-menu-item-text',
28617 cls : 'fa ' + this.icon
28626 href : this.href || '#',
28633 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28637 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28639 if(this.pos == 'left'){
28640 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28647 initEvents : function()
28649 this.el.on('mouseover', this.onMouseOver, this);
28650 this.el.on('mouseout', this.onMouseOut, this);
28652 this.el.select('a', true).first().on('click', this.onClick, this);
28656 onClick : function(e)
28658 if(this.preventDefault){
28659 e.preventDefault();
28662 this.fireEvent("click", this, e);
28665 onMouseOver : function(e)
28667 if(this.submenu && this.pos == 'left'){
28668 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28671 this.fireEvent("mouseover", this, e);
28674 onMouseOut : function(e)
28676 this.fireEvent("mouseout", this, e);
28688 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28691 * @class Roo.bootstrap.menu.Separator
28692 * @extends Roo.bootstrap.Component
28693 * Bootstrap Separator class
28696 * Create a new Separator
28697 * @param {Object} config The config object
28701 Roo.bootstrap.menu.Separator = function(config){
28702 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28705 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28707 getAutoCreate : function(){
28728 * @class Roo.bootstrap.Tooltip
28729 * Bootstrap Tooltip class
28730 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28731 * to determine which dom element triggers the tooltip.
28733 * It needs to add support for additional attributes like tooltip-position
28736 * Create a new Toolti
28737 * @param {Object} config The config object
28740 Roo.bootstrap.Tooltip = function(config){
28741 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28743 this.alignment = Roo.bootstrap.Tooltip.alignment;
28745 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28746 this.alignment = config.alignment;
28751 Roo.apply(Roo.bootstrap.Tooltip, {
28753 * @function init initialize tooltip monitoring.
28757 currentTip : false,
28758 currentRegion : false,
28764 Roo.get(document).on('mouseover', this.enter ,this);
28765 Roo.get(document).on('mouseout', this.leave, this);
28768 this.currentTip = new Roo.bootstrap.Tooltip();
28771 enter : function(ev)
28773 var dom = ev.getTarget();
28775 //Roo.log(['enter',dom]);
28776 var el = Roo.fly(dom);
28777 if (this.currentEl) {
28779 //Roo.log(this.currentEl);
28780 //Roo.log(this.currentEl.contains(dom));
28781 if (this.currentEl == el) {
28784 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28790 if (this.currentTip.el) {
28791 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28795 if(!el || el.dom == document){
28801 // you can not look for children, as if el is the body.. then everythign is the child..
28802 if (!el.attr('tooltip')) { //
28803 if (!el.select("[tooltip]").elements.length) {
28806 // is the mouse over this child...?
28807 bindEl = el.select("[tooltip]").first();
28808 var xy = ev.getXY();
28809 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28810 //Roo.log("not in region.");
28813 //Roo.log("child element over..");
28816 this.currentEl = bindEl;
28817 this.currentTip.bind(bindEl);
28818 this.currentRegion = Roo.lib.Region.getRegion(dom);
28819 this.currentTip.enter();
28822 leave : function(ev)
28824 var dom = ev.getTarget();
28825 //Roo.log(['leave',dom]);
28826 if (!this.currentEl) {
28831 if (dom != this.currentEl.dom) {
28834 var xy = ev.getXY();
28835 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28838 // only activate leave if mouse cursor is outside... bounding box..
28843 if (this.currentTip) {
28844 this.currentTip.leave();
28846 //Roo.log('clear currentEl');
28847 this.currentEl = false;
28852 'left' : ['r-l', [-2,0], 'right'],
28853 'right' : ['l-r', [2,0], 'left'],
28854 'bottom' : ['t-b', [0,2], 'top'],
28855 'top' : [ 'b-t', [0,-2], 'bottom']
28861 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28866 delay : null, // can be { show : 300 , hide: 500}
28870 hoverState : null, //???
28872 placement : 'bottom',
28876 getAutoCreate : function(){
28883 cls : 'tooltip-arrow arrow'
28886 cls : 'tooltip-inner'
28893 bind : function(el)
28898 initEvents : function()
28900 this.arrowEl = this.el.select('.arrow', true).first();
28901 this.innerEl = this.el.select('.tooltip-inner', true).first();
28904 enter : function () {
28906 if (this.timeout != null) {
28907 clearTimeout(this.timeout);
28910 this.hoverState = 'in';
28911 //Roo.log("enter - show");
28912 if (!this.delay || !this.delay.show) {
28917 this.timeout = setTimeout(function () {
28918 if (_t.hoverState == 'in') {
28921 }, this.delay.show);
28925 clearTimeout(this.timeout);
28927 this.hoverState = 'out';
28928 if (!this.delay || !this.delay.hide) {
28934 this.timeout = setTimeout(function () {
28935 //Roo.log("leave - timeout");
28937 if (_t.hoverState == 'out') {
28939 Roo.bootstrap.Tooltip.currentEl = false;
28944 show : function (msg)
28947 this.render(document.body);
28950 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28952 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28954 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28956 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28957 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28959 var placement = typeof this.placement == 'function' ?
28960 this.placement.call(this, this.el, on_el) :
28963 var autoToken = /\s?auto?\s?/i;
28964 var autoPlace = autoToken.test(placement);
28966 placement = placement.replace(autoToken, '') || 'top';
28970 //this.el.setXY([0,0]);
28972 //this.el.dom.style.display='block';
28974 //this.el.appendTo(on_el);
28976 var p = this.getPosition();
28977 var box = this.el.getBox();
28983 var align = this.alignment[placement];
28985 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28987 if(placement == 'top' || placement == 'bottom'){
28989 placement = 'right';
28992 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28993 placement = 'left';
28996 var scroll = Roo.select('body', true).first().getScroll();
28998 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29002 align = this.alignment[placement];
29004 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29008 this.el.alignTo(this.bindEl, align[0],align[1]);
29009 //var arrow = this.el.select('.arrow',true).first();
29010 //arrow.set(align[2],
29012 this.el.addClass(placement);
29013 this.el.addClass("bs-tooltip-"+ placement);
29015 this.el.addClass('in fade show');
29017 this.hoverState = null;
29019 if (this.el.hasClass('fade')) {
29034 //this.el.setXY([0,0]);
29035 this.el.removeClass(['show', 'in']);
29051 * @class Roo.bootstrap.LocationPicker
29052 * @extends Roo.bootstrap.Component
29053 * Bootstrap LocationPicker class
29054 * @cfg {Number} latitude Position when init default 0
29055 * @cfg {Number} longitude Position when init default 0
29056 * @cfg {Number} zoom default 15
29057 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29058 * @cfg {Boolean} mapTypeControl default false
29059 * @cfg {Boolean} disableDoubleClickZoom default false
29060 * @cfg {Boolean} scrollwheel default true
29061 * @cfg {Boolean} streetViewControl default false
29062 * @cfg {Number} radius default 0
29063 * @cfg {String} locationName
29064 * @cfg {Boolean} draggable default true
29065 * @cfg {Boolean} enableAutocomplete default false
29066 * @cfg {Boolean} enableReverseGeocode default true
29067 * @cfg {String} markerTitle
29070 * Create a new LocationPicker
29071 * @param {Object} config The config object
29075 Roo.bootstrap.LocationPicker = function(config){
29077 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29082 * Fires when the picker initialized.
29083 * @param {Roo.bootstrap.LocationPicker} this
29084 * @param {Google Location} location
29088 * @event positionchanged
29089 * Fires when the picker position changed.
29090 * @param {Roo.bootstrap.LocationPicker} this
29091 * @param {Google Location} location
29093 positionchanged : true,
29096 * Fires when the map resize.
29097 * @param {Roo.bootstrap.LocationPicker} this
29102 * Fires when the map show.
29103 * @param {Roo.bootstrap.LocationPicker} this
29108 * Fires when the map hide.
29109 * @param {Roo.bootstrap.LocationPicker} this
29114 * Fires when click the map.
29115 * @param {Roo.bootstrap.LocationPicker} this
29116 * @param {Map event} e
29120 * @event mapRightClick
29121 * Fires when right click the map.
29122 * @param {Roo.bootstrap.LocationPicker} this
29123 * @param {Map event} e
29125 mapRightClick : true,
29127 * @event markerClick
29128 * Fires when click the marker.
29129 * @param {Roo.bootstrap.LocationPicker} this
29130 * @param {Map event} e
29132 markerClick : true,
29134 * @event markerRightClick
29135 * Fires when right click the marker.
29136 * @param {Roo.bootstrap.LocationPicker} this
29137 * @param {Map event} e
29139 markerRightClick : true,
29141 * @event OverlayViewDraw
29142 * Fires when OverlayView Draw
29143 * @param {Roo.bootstrap.LocationPicker} this
29145 OverlayViewDraw : true,
29147 * @event OverlayViewOnAdd
29148 * Fires when OverlayView Draw
29149 * @param {Roo.bootstrap.LocationPicker} this
29151 OverlayViewOnAdd : true,
29153 * @event OverlayViewOnRemove
29154 * Fires when OverlayView Draw
29155 * @param {Roo.bootstrap.LocationPicker} this
29157 OverlayViewOnRemove : true,
29159 * @event OverlayViewShow
29160 * Fires when OverlayView Draw
29161 * @param {Roo.bootstrap.LocationPicker} this
29162 * @param {Pixel} cpx
29164 OverlayViewShow : true,
29166 * @event OverlayViewHide
29167 * Fires when OverlayView Draw
29168 * @param {Roo.bootstrap.LocationPicker} this
29170 OverlayViewHide : true,
29172 * @event loadexception
29173 * Fires when load google lib failed.
29174 * @param {Roo.bootstrap.LocationPicker} this
29176 loadexception : true
29181 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29183 gMapContext: false,
29189 mapTypeControl: false,
29190 disableDoubleClickZoom: false,
29192 streetViewControl: false,
29196 enableAutocomplete: false,
29197 enableReverseGeocode: true,
29200 getAutoCreate: function()
29205 cls: 'roo-location-picker'
29211 initEvents: function(ct, position)
29213 if(!this.el.getWidth() || this.isApplied()){
29217 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29222 initial: function()
29224 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29225 this.fireEvent('loadexception', this);
29229 if(!this.mapTypeId){
29230 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29233 this.gMapContext = this.GMapContext();
29235 this.initOverlayView();
29237 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29241 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29242 _this.setPosition(_this.gMapContext.marker.position);
29245 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29246 _this.fireEvent('mapClick', this, event);
29250 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29251 _this.fireEvent('mapRightClick', this, event);
29255 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29256 _this.fireEvent('markerClick', this, event);
29260 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29261 _this.fireEvent('markerRightClick', this, event);
29265 this.setPosition(this.gMapContext.location);
29267 this.fireEvent('initial', this, this.gMapContext.location);
29270 initOverlayView: function()
29274 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29278 _this.fireEvent('OverlayViewDraw', _this);
29283 _this.fireEvent('OverlayViewOnAdd', _this);
29286 onRemove: function()
29288 _this.fireEvent('OverlayViewOnRemove', _this);
29291 show: function(cpx)
29293 _this.fireEvent('OverlayViewShow', _this, cpx);
29298 _this.fireEvent('OverlayViewHide', _this);
29304 fromLatLngToContainerPixel: function(event)
29306 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29309 isApplied: function()
29311 return this.getGmapContext() == false ? false : true;
29314 getGmapContext: function()
29316 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29319 GMapContext: function()
29321 var position = new google.maps.LatLng(this.latitude, this.longitude);
29323 var _map = new google.maps.Map(this.el.dom, {
29326 mapTypeId: this.mapTypeId,
29327 mapTypeControl: this.mapTypeControl,
29328 disableDoubleClickZoom: this.disableDoubleClickZoom,
29329 scrollwheel: this.scrollwheel,
29330 streetViewControl: this.streetViewControl,
29331 locationName: this.locationName,
29332 draggable: this.draggable,
29333 enableAutocomplete: this.enableAutocomplete,
29334 enableReverseGeocode: this.enableReverseGeocode
29337 var _marker = new google.maps.Marker({
29338 position: position,
29340 title: this.markerTitle,
29341 draggable: this.draggable
29348 location: position,
29349 radius: this.radius,
29350 locationName: this.locationName,
29351 addressComponents: {
29352 formatted_address: null,
29353 addressLine1: null,
29354 addressLine2: null,
29356 streetNumber: null,
29360 stateOrProvince: null
29363 domContainer: this.el.dom,
29364 geodecoder: new google.maps.Geocoder()
29368 drawCircle: function(center, radius, options)
29370 if (this.gMapContext.circle != null) {
29371 this.gMapContext.circle.setMap(null);
29375 options = Roo.apply({}, options, {
29376 strokeColor: "#0000FF",
29377 strokeOpacity: .35,
29379 fillColor: "#0000FF",
29383 options.map = this.gMapContext.map;
29384 options.radius = radius;
29385 options.center = center;
29386 this.gMapContext.circle = new google.maps.Circle(options);
29387 return this.gMapContext.circle;
29393 setPosition: function(location)
29395 this.gMapContext.location = location;
29396 this.gMapContext.marker.setPosition(location);
29397 this.gMapContext.map.panTo(location);
29398 this.drawCircle(location, this.gMapContext.radius, {});
29402 if (this.gMapContext.settings.enableReverseGeocode) {
29403 this.gMapContext.geodecoder.geocode({
29404 latLng: this.gMapContext.location
29405 }, function(results, status) {
29407 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29408 _this.gMapContext.locationName = results[0].formatted_address;
29409 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29411 _this.fireEvent('positionchanged', this, location);
29418 this.fireEvent('positionchanged', this, location);
29423 google.maps.event.trigger(this.gMapContext.map, "resize");
29425 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29427 this.fireEvent('resize', this);
29430 setPositionByLatLng: function(latitude, longitude)
29432 this.setPosition(new google.maps.LatLng(latitude, longitude));
29435 getCurrentPosition: function()
29438 latitude: this.gMapContext.location.lat(),
29439 longitude: this.gMapContext.location.lng()
29443 getAddressName: function()
29445 return this.gMapContext.locationName;
29448 getAddressComponents: function()
29450 return this.gMapContext.addressComponents;
29453 address_component_from_google_geocode: function(address_components)
29457 for (var i = 0; i < address_components.length; i++) {
29458 var component = address_components[i];
29459 if (component.types.indexOf("postal_code") >= 0) {
29460 result.postalCode = component.short_name;
29461 } else if (component.types.indexOf("street_number") >= 0) {
29462 result.streetNumber = component.short_name;
29463 } else if (component.types.indexOf("route") >= 0) {
29464 result.streetName = component.short_name;
29465 } else if (component.types.indexOf("neighborhood") >= 0) {
29466 result.city = component.short_name;
29467 } else if (component.types.indexOf("locality") >= 0) {
29468 result.city = component.short_name;
29469 } else if (component.types.indexOf("sublocality") >= 0) {
29470 result.district = component.short_name;
29471 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29472 result.stateOrProvince = component.short_name;
29473 } else if (component.types.indexOf("country") >= 0) {
29474 result.country = component.short_name;
29478 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29479 result.addressLine2 = "";
29483 setZoomLevel: function(zoom)
29485 this.gMapContext.map.setZoom(zoom);
29498 this.fireEvent('show', this);
29509 this.fireEvent('hide', this);
29514 Roo.apply(Roo.bootstrap.LocationPicker, {
29516 OverlayView : function(map, options)
29518 options = options || {};
29525 * @class Roo.bootstrap.Alert
29526 * @extends Roo.bootstrap.Component
29527 * Bootstrap Alert class - shows an alert area box
29529 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29530 Enter a valid email address
29533 * @cfg {String} title The title of alert
29534 * @cfg {String} html The content of alert
29535 * @cfg {String} weight ( success | info | warning | danger )
29536 * @cfg {String} faicon font-awesomeicon
29539 * Create a new alert
29540 * @param {Object} config The config object
29544 Roo.bootstrap.Alert = function(config){
29545 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29549 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29556 getAutoCreate : function()
29565 cls : 'roo-alert-icon'
29570 cls : 'roo-alert-title',
29575 cls : 'roo-alert-text',
29582 cfg.cn[0].cls += ' fa ' + this.faicon;
29586 cfg.cls += ' alert-' + this.weight;
29592 initEvents: function()
29594 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29597 setTitle : function(str)
29599 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29602 setText : function(str)
29604 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29607 setWeight : function(weight)
29610 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29613 this.weight = weight;
29615 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29618 setIcon : function(icon)
29621 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29624 this.faicon = icon;
29626 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29647 * @class Roo.bootstrap.UploadCropbox
29648 * @extends Roo.bootstrap.Component
29649 * Bootstrap UploadCropbox class
29650 * @cfg {String} emptyText show when image has been loaded
29651 * @cfg {String} rotateNotify show when image too small to rotate
29652 * @cfg {Number} errorTimeout default 3000
29653 * @cfg {Number} minWidth default 300
29654 * @cfg {Number} minHeight default 300
29655 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29656 * @cfg {Boolean} isDocument (true|false) default false
29657 * @cfg {String} url action url
29658 * @cfg {String} paramName default 'imageUpload'
29659 * @cfg {String} method default POST
29660 * @cfg {Boolean} loadMask (true|false) default true
29661 * @cfg {Boolean} loadingText default 'Loading...'
29664 * Create a new UploadCropbox
29665 * @param {Object} config The config object
29668 Roo.bootstrap.UploadCropbox = function(config){
29669 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29673 * @event beforeselectfile
29674 * Fire before select file
29675 * @param {Roo.bootstrap.UploadCropbox} this
29677 "beforeselectfile" : true,
29680 * Fire after initEvent
29681 * @param {Roo.bootstrap.UploadCropbox} this
29686 * Fire after initEvent
29687 * @param {Roo.bootstrap.UploadCropbox} this
29688 * @param {String} data
29693 * Fire when preparing the file data
29694 * @param {Roo.bootstrap.UploadCropbox} this
29695 * @param {Object} file
29700 * Fire when get exception
29701 * @param {Roo.bootstrap.UploadCropbox} this
29702 * @param {XMLHttpRequest} xhr
29704 "exception" : true,
29706 * @event beforeloadcanvas
29707 * Fire before load the canvas
29708 * @param {Roo.bootstrap.UploadCropbox} this
29709 * @param {String} src
29711 "beforeloadcanvas" : true,
29714 * Fire when trash image
29715 * @param {Roo.bootstrap.UploadCropbox} this
29720 * Fire when download the image
29721 * @param {Roo.bootstrap.UploadCropbox} this
29725 * @event footerbuttonclick
29726 * Fire when footerbuttonclick
29727 * @param {Roo.bootstrap.UploadCropbox} this
29728 * @param {String} type
29730 "footerbuttonclick" : true,
29734 * @param {Roo.bootstrap.UploadCropbox} this
29739 * Fire when rotate the image
29740 * @param {Roo.bootstrap.UploadCropbox} this
29741 * @param {String} pos
29746 * Fire when inspect the file
29747 * @param {Roo.bootstrap.UploadCropbox} this
29748 * @param {Object} file
29753 * Fire when xhr upload the file
29754 * @param {Roo.bootstrap.UploadCropbox} this
29755 * @param {Object} data
29760 * Fire when arrange the file data
29761 * @param {Roo.bootstrap.UploadCropbox} this
29762 * @param {Object} formData
29767 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29770 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29772 emptyText : 'Click to upload image',
29773 rotateNotify : 'Image is too small to rotate',
29774 errorTimeout : 3000,
29788 cropType : 'image/jpeg',
29790 canvasLoaded : false,
29791 isDocument : false,
29793 paramName : 'imageUpload',
29795 loadingText : 'Loading...',
29798 getAutoCreate : function()
29802 cls : 'roo-upload-cropbox',
29806 cls : 'roo-upload-cropbox-selector',
29811 cls : 'roo-upload-cropbox-body',
29812 style : 'cursor:pointer',
29816 cls : 'roo-upload-cropbox-preview'
29820 cls : 'roo-upload-cropbox-thumb'
29824 cls : 'roo-upload-cropbox-empty-notify',
29825 html : this.emptyText
29829 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29830 html : this.rotateNotify
29836 cls : 'roo-upload-cropbox-footer',
29839 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29849 onRender : function(ct, position)
29851 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29853 if (this.buttons.length) {
29855 Roo.each(this.buttons, function(bb) {
29857 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29859 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29865 this.maskEl = this.el;
29869 initEvents : function()
29871 this.urlAPI = (window.createObjectURL && window) ||
29872 (window.URL && URL.revokeObjectURL && URL) ||
29873 (window.webkitURL && webkitURL);
29875 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29876 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29878 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29879 this.selectorEl.hide();
29881 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29882 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29884 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29885 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29886 this.thumbEl.hide();
29888 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29889 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29891 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29892 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29893 this.errorEl.hide();
29895 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29896 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29897 this.footerEl.hide();
29899 this.setThumbBoxSize();
29905 this.fireEvent('initial', this);
29912 window.addEventListener("resize", function() { _this.resize(); } );
29914 this.bodyEl.on('click', this.beforeSelectFile, this);
29917 this.bodyEl.on('touchstart', this.onTouchStart, this);
29918 this.bodyEl.on('touchmove', this.onTouchMove, this);
29919 this.bodyEl.on('touchend', this.onTouchEnd, this);
29923 this.bodyEl.on('mousedown', this.onMouseDown, this);
29924 this.bodyEl.on('mousemove', this.onMouseMove, this);
29925 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29926 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29927 Roo.get(document).on('mouseup', this.onMouseUp, this);
29930 this.selectorEl.on('change', this.onFileSelected, this);
29936 this.baseScale = 1;
29938 this.baseRotate = 1;
29939 this.dragable = false;
29940 this.pinching = false;
29943 this.cropData = false;
29944 this.notifyEl.dom.innerHTML = this.emptyText;
29946 this.selectorEl.dom.value = '';
29950 resize : function()
29952 if(this.fireEvent('resize', this) != false){
29953 this.setThumbBoxPosition();
29954 this.setCanvasPosition();
29958 onFooterButtonClick : function(e, el, o, type)
29961 case 'rotate-left' :
29962 this.onRotateLeft(e);
29964 case 'rotate-right' :
29965 this.onRotateRight(e);
29968 this.beforeSelectFile(e);
29983 this.fireEvent('footerbuttonclick', this, type);
29986 beforeSelectFile : function(e)
29988 e.preventDefault();
29990 if(this.fireEvent('beforeselectfile', this) != false){
29991 this.selectorEl.dom.click();
29995 onFileSelected : function(e)
29997 e.preventDefault();
29999 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30003 var file = this.selectorEl.dom.files[0];
30005 if(this.fireEvent('inspect', this, file) != false){
30006 this.prepare(file);
30011 trash : function(e)
30013 this.fireEvent('trash', this);
30016 download : function(e)
30018 this.fireEvent('download', this);
30021 loadCanvas : function(src)
30023 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30027 this.imageEl = document.createElement('img');
30031 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30033 this.imageEl.src = src;
30037 onLoadCanvas : function()
30039 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30040 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30042 this.bodyEl.un('click', this.beforeSelectFile, this);
30044 this.notifyEl.hide();
30045 this.thumbEl.show();
30046 this.footerEl.show();
30048 this.baseRotateLevel();
30050 if(this.isDocument){
30051 this.setThumbBoxSize();
30054 this.setThumbBoxPosition();
30056 this.baseScaleLevel();
30062 this.canvasLoaded = true;
30065 this.maskEl.unmask();
30070 setCanvasPosition : function()
30072 if(!this.canvasEl){
30076 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30077 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30079 this.previewEl.setLeft(pw);
30080 this.previewEl.setTop(ph);
30084 onMouseDown : function(e)
30088 this.dragable = true;
30089 this.pinching = false;
30091 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30092 this.dragable = false;
30096 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30097 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30101 onMouseMove : function(e)
30105 if(!this.canvasLoaded){
30109 if (!this.dragable){
30113 var minX = Math.ceil(this.thumbEl.getLeft(true));
30114 var minY = Math.ceil(this.thumbEl.getTop(true));
30116 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30117 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30119 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30120 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30122 x = x - this.mouseX;
30123 y = y - this.mouseY;
30125 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30126 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30128 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30129 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30131 this.previewEl.setLeft(bgX);
30132 this.previewEl.setTop(bgY);
30134 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30135 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30138 onMouseUp : function(e)
30142 this.dragable = false;
30145 onMouseWheel : function(e)
30149 this.startScale = this.scale;
30151 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30153 if(!this.zoomable()){
30154 this.scale = this.startScale;
30163 zoomable : function()
30165 var minScale = this.thumbEl.getWidth() / this.minWidth;
30167 if(this.minWidth < this.minHeight){
30168 minScale = this.thumbEl.getHeight() / this.minHeight;
30171 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30172 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30176 (this.rotate == 0 || this.rotate == 180) &&
30178 width > this.imageEl.OriginWidth ||
30179 height > this.imageEl.OriginHeight ||
30180 (width < this.minWidth && height < this.minHeight)
30188 (this.rotate == 90 || this.rotate == 270) &&
30190 width > this.imageEl.OriginWidth ||
30191 height > this.imageEl.OriginHeight ||
30192 (width < this.minHeight && height < this.minWidth)
30199 !this.isDocument &&
30200 (this.rotate == 0 || this.rotate == 180) &&
30202 width < this.minWidth ||
30203 width > this.imageEl.OriginWidth ||
30204 height < this.minHeight ||
30205 height > this.imageEl.OriginHeight
30212 !this.isDocument &&
30213 (this.rotate == 90 || this.rotate == 270) &&
30215 width < this.minHeight ||
30216 width > this.imageEl.OriginWidth ||
30217 height < this.minWidth ||
30218 height > this.imageEl.OriginHeight
30228 onRotateLeft : function(e)
30230 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30232 var minScale = this.thumbEl.getWidth() / this.minWidth;
30234 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30235 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30237 this.startScale = this.scale;
30239 while (this.getScaleLevel() < minScale){
30241 this.scale = this.scale + 1;
30243 if(!this.zoomable()){
30248 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30249 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30254 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30261 this.scale = this.startScale;
30263 this.onRotateFail();
30268 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30270 if(this.isDocument){
30271 this.setThumbBoxSize();
30272 this.setThumbBoxPosition();
30273 this.setCanvasPosition();
30278 this.fireEvent('rotate', this, 'left');
30282 onRotateRight : function(e)
30284 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30286 var minScale = this.thumbEl.getWidth() / this.minWidth;
30288 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30289 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30291 this.startScale = this.scale;
30293 while (this.getScaleLevel() < minScale){
30295 this.scale = this.scale + 1;
30297 if(!this.zoomable()){
30302 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30303 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30308 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30315 this.scale = this.startScale;
30317 this.onRotateFail();
30322 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30324 if(this.isDocument){
30325 this.setThumbBoxSize();
30326 this.setThumbBoxPosition();
30327 this.setCanvasPosition();
30332 this.fireEvent('rotate', this, 'right');
30335 onRotateFail : function()
30337 this.errorEl.show(true);
30341 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30346 this.previewEl.dom.innerHTML = '';
30348 var canvasEl = document.createElement("canvas");
30350 var contextEl = canvasEl.getContext("2d");
30352 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30353 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30354 var center = this.imageEl.OriginWidth / 2;
30356 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30357 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30358 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30359 center = this.imageEl.OriginHeight / 2;
30362 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30364 contextEl.translate(center, center);
30365 contextEl.rotate(this.rotate * Math.PI / 180);
30367 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30369 this.canvasEl = document.createElement("canvas");
30371 this.contextEl = this.canvasEl.getContext("2d");
30373 switch (this.rotate) {
30376 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30377 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30379 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30384 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30385 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30387 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30388 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);
30392 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30397 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30398 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30400 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30401 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);
30405 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);
30410 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30411 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30413 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30414 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30418 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);
30425 this.previewEl.appendChild(this.canvasEl);
30427 this.setCanvasPosition();
30432 if(!this.canvasLoaded){
30436 var imageCanvas = document.createElement("canvas");
30438 var imageContext = imageCanvas.getContext("2d");
30440 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30441 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30443 var center = imageCanvas.width / 2;
30445 imageContext.translate(center, center);
30447 imageContext.rotate(this.rotate * Math.PI / 180);
30449 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30451 var canvas = document.createElement("canvas");
30453 var context = canvas.getContext("2d");
30455 canvas.width = this.minWidth;
30456 canvas.height = this.minHeight;
30458 switch (this.rotate) {
30461 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30462 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30464 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30465 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30467 var targetWidth = this.minWidth - 2 * x;
30468 var targetHeight = this.minHeight - 2 * y;
30472 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30473 scale = targetWidth / width;
30476 if(x > 0 && y == 0){
30477 scale = targetHeight / height;
30480 if(x > 0 && y > 0){
30481 scale = targetWidth / width;
30483 if(width < height){
30484 scale = targetHeight / height;
30488 context.scale(scale, scale);
30490 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30491 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30493 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30494 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30496 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30501 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30502 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30504 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30505 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30507 var targetWidth = this.minWidth - 2 * x;
30508 var targetHeight = this.minHeight - 2 * y;
30512 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30513 scale = targetWidth / width;
30516 if(x > 0 && y == 0){
30517 scale = targetHeight / height;
30520 if(x > 0 && y > 0){
30521 scale = targetWidth / width;
30523 if(width < height){
30524 scale = targetHeight / height;
30528 context.scale(scale, scale);
30530 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30531 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30533 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30534 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30536 sx += (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.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30544 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (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 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30579 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30581 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30586 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30587 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30589 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30590 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30592 var targetWidth = this.minWidth - 2 * x;
30593 var targetHeight = this.minHeight - 2 * y;
30597 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30598 scale = targetWidth / width;
30601 if(x > 0 && y == 0){
30602 scale = targetHeight / height;
30605 if(x > 0 && y > 0){
30606 scale = targetWidth / width;
30608 if(width < height){
30609 scale = targetHeight / height;
30613 context.scale(scale, scale);
30615 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30616 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30618 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30619 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30621 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30623 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30630 this.cropData = canvas.toDataURL(this.cropType);
30632 if(this.fireEvent('crop', this, this.cropData) !== false){
30633 this.process(this.file, this.cropData);
30640 setThumbBoxSize : function()
30644 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30645 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30646 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30648 this.minWidth = width;
30649 this.minHeight = height;
30651 if(this.rotate == 90 || this.rotate == 270){
30652 this.minWidth = height;
30653 this.minHeight = width;
30658 width = Math.ceil(this.minWidth * height / this.minHeight);
30660 if(this.minWidth > this.minHeight){
30662 height = Math.ceil(this.minHeight * width / this.minWidth);
30665 this.thumbEl.setStyle({
30666 width : width + 'px',
30667 height : height + 'px'
30674 setThumbBoxPosition : function()
30676 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30677 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30679 this.thumbEl.setLeft(x);
30680 this.thumbEl.setTop(y);
30684 baseRotateLevel : function()
30686 this.baseRotate = 1;
30689 typeof(this.exif) != 'undefined' &&
30690 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30691 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30693 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30696 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30700 baseScaleLevel : function()
30704 if(this.isDocument){
30706 if(this.baseRotate == 6 || this.baseRotate == 8){
30708 height = this.thumbEl.getHeight();
30709 this.baseScale = height / this.imageEl.OriginWidth;
30711 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30712 width = this.thumbEl.getWidth();
30713 this.baseScale = width / this.imageEl.OriginHeight;
30719 height = this.thumbEl.getHeight();
30720 this.baseScale = height / this.imageEl.OriginHeight;
30722 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30723 width = this.thumbEl.getWidth();
30724 this.baseScale = width / this.imageEl.OriginWidth;
30730 if(this.baseRotate == 6 || this.baseRotate == 8){
30732 width = this.thumbEl.getHeight();
30733 this.baseScale = width / this.imageEl.OriginHeight;
30735 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30736 height = this.thumbEl.getWidth();
30737 this.baseScale = height / this.imageEl.OriginHeight;
30740 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30741 height = this.thumbEl.getWidth();
30742 this.baseScale = height / this.imageEl.OriginHeight;
30744 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30745 width = this.thumbEl.getHeight();
30746 this.baseScale = width / this.imageEl.OriginWidth;
30753 width = this.thumbEl.getWidth();
30754 this.baseScale = width / this.imageEl.OriginWidth;
30756 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30757 height = this.thumbEl.getHeight();
30758 this.baseScale = height / this.imageEl.OriginHeight;
30761 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30763 height = this.thumbEl.getHeight();
30764 this.baseScale = height / this.imageEl.OriginHeight;
30766 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30767 width = this.thumbEl.getWidth();
30768 this.baseScale = width / this.imageEl.OriginWidth;
30776 getScaleLevel : function()
30778 return this.baseScale * Math.pow(1.1, this.scale);
30781 onTouchStart : function(e)
30783 if(!this.canvasLoaded){
30784 this.beforeSelectFile(e);
30788 var touches = e.browserEvent.touches;
30794 if(touches.length == 1){
30795 this.onMouseDown(e);
30799 if(touches.length != 2){
30805 for(var i = 0, finger; finger = touches[i]; i++){
30806 coords.push(finger.pageX, finger.pageY);
30809 var x = Math.pow(coords[0] - coords[2], 2);
30810 var y = Math.pow(coords[1] - coords[3], 2);
30812 this.startDistance = Math.sqrt(x + y);
30814 this.startScale = this.scale;
30816 this.pinching = true;
30817 this.dragable = false;
30821 onTouchMove : function(e)
30823 if(!this.pinching && !this.dragable){
30827 var touches = e.browserEvent.touches;
30834 this.onMouseMove(e);
30840 for(var i = 0, finger; finger = touches[i]; i++){
30841 coords.push(finger.pageX, finger.pageY);
30844 var x = Math.pow(coords[0] - coords[2], 2);
30845 var y = Math.pow(coords[1] - coords[3], 2);
30847 this.endDistance = Math.sqrt(x + y);
30849 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30851 if(!this.zoomable()){
30852 this.scale = this.startScale;
30860 onTouchEnd : function(e)
30862 this.pinching = false;
30863 this.dragable = false;
30867 process : function(file, crop)
30870 this.maskEl.mask(this.loadingText);
30873 this.xhr = new XMLHttpRequest();
30875 file.xhr = this.xhr;
30877 this.xhr.open(this.method, this.url, true);
30880 "Accept": "application/json",
30881 "Cache-Control": "no-cache",
30882 "X-Requested-With": "XMLHttpRequest"
30885 for (var headerName in headers) {
30886 var headerValue = headers[headerName];
30888 this.xhr.setRequestHeader(headerName, headerValue);
30894 this.xhr.onload = function()
30896 _this.xhrOnLoad(_this.xhr);
30899 this.xhr.onerror = function()
30901 _this.xhrOnError(_this.xhr);
30904 var formData = new FormData();
30906 formData.append('returnHTML', 'NO');
30909 formData.append('crop', crop);
30912 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30913 formData.append(this.paramName, file, file.name);
30916 if(typeof(file.filename) != 'undefined'){
30917 formData.append('filename', file.filename);
30920 if(typeof(file.mimetype) != 'undefined'){
30921 formData.append('mimetype', file.mimetype);
30924 if(this.fireEvent('arrange', this, formData) != false){
30925 this.xhr.send(formData);
30929 xhrOnLoad : function(xhr)
30932 this.maskEl.unmask();
30935 if (xhr.readyState !== 4) {
30936 this.fireEvent('exception', this, xhr);
30940 var response = Roo.decode(xhr.responseText);
30942 if(!response.success){
30943 this.fireEvent('exception', this, xhr);
30947 var response = Roo.decode(xhr.responseText);
30949 this.fireEvent('upload', this, response);
30953 xhrOnError : function()
30956 this.maskEl.unmask();
30959 Roo.log('xhr on error');
30961 var response = Roo.decode(xhr.responseText);
30967 prepare : function(file)
30970 this.maskEl.mask(this.loadingText);
30976 if(typeof(file) === 'string'){
30977 this.loadCanvas(file);
30981 if(!file || !this.urlAPI){
30986 this.cropType = file.type;
30990 if(this.fireEvent('prepare', this, this.file) != false){
30992 var reader = new FileReader();
30994 reader.onload = function (e) {
30995 if (e.target.error) {
30996 Roo.log(e.target.error);
31000 var buffer = e.target.result,
31001 dataView = new DataView(buffer),
31003 maxOffset = dataView.byteLength - 4,
31007 if (dataView.getUint16(0) === 0xffd8) {
31008 while (offset < maxOffset) {
31009 markerBytes = dataView.getUint16(offset);
31011 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31012 markerLength = dataView.getUint16(offset + 2) + 2;
31013 if (offset + markerLength > dataView.byteLength) {
31014 Roo.log('Invalid meta data: Invalid segment size.');
31018 if(markerBytes == 0xffe1){
31019 _this.parseExifData(
31026 offset += markerLength;
31036 var url = _this.urlAPI.createObjectURL(_this.file);
31038 _this.loadCanvas(url);
31043 reader.readAsArrayBuffer(this.file);
31049 parseExifData : function(dataView, offset, length)
31051 var tiffOffset = offset + 10,
31055 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31056 // No Exif data, might be XMP data instead
31060 // Check for the ASCII code for "Exif" (0x45786966):
31061 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31062 // No Exif data, might be XMP data instead
31065 if (tiffOffset + 8 > dataView.byteLength) {
31066 Roo.log('Invalid Exif data: Invalid segment size.');
31069 // Check for the two null bytes:
31070 if (dataView.getUint16(offset + 8) !== 0x0000) {
31071 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31074 // Check the byte alignment:
31075 switch (dataView.getUint16(tiffOffset)) {
31077 littleEndian = true;
31080 littleEndian = false;
31083 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31086 // Check for the TIFF tag marker (0x002A):
31087 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31088 Roo.log('Invalid Exif data: Missing TIFF marker.');
31091 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31092 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31094 this.parseExifTags(
31097 tiffOffset + dirOffset,
31102 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31107 if (dirOffset + 6 > dataView.byteLength) {
31108 Roo.log('Invalid Exif data: Invalid directory offset.');
31111 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31112 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31113 if (dirEndOffset + 4 > dataView.byteLength) {
31114 Roo.log('Invalid Exif data: Invalid directory size.');
31117 for (i = 0; i < tagsNumber; i += 1) {
31121 dirOffset + 2 + 12 * i, // tag offset
31125 // Return the offset to the next directory:
31126 return dataView.getUint32(dirEndOffset, littleEndian);
31129 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31131 var tag = dataView.getUint16(offset, littleEndian);
31133 this.exif[tag] = this.getExifValue(
31137 dataView.getUint16(offset + 2, littleEndian), // tag type
31138 dataView.getUint32(offset + 4, littleEndian), // tag length
31143 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31145 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31154 Roo.log('Invalid Exif data: Invalid tag type.');
31158 tagSize = tagType.size * length;
31159 // Determine if the value is contained in the dataOffset bytes,
31160 // or if the value at the dataOffset is a pointer to the actual data:
31161 dataOffset = tagSize > 4 ?
31162 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31163 if (dataOffset + tagSize > dataView.byteLength) {
31164 Roo.log('Invalid Exif data: Invalid data offset.');
31167 if (length === 1) {
31168 return tagType.getValue(dataView, dataOffset, littleEndian);
31171 for (i = 0; i < length; i += 1) {
31172 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31175 if (tagType.ascii) {
31177 // Concatenate the chars:
31178 for (i = 0; i < values.length; i += 1) {
31180 // Ignore the terminating NULL byte(s):
31181 if (c === '\u0000') {
31193 Roo.apply(Roo.bootstrap.UploadCropbox, {
31195 'Orientation': 0x0112
31199 1: 0, //'top-left',
31201 3: 180, //'bottom-right',
31202 // 4: 'bottom-left',
31204 6: 90, //'right-top',
31205 // 7: 'right-bottom',
31206 8: 270 //'left-bottom'
31210 // byte, 8-bit unsigned int:
31212 getValue: function (dataView, dataOffset) {
31213 return dataView.getUint8(dataOffset);
31217 // ascii, 8-bit byte:
31219 getValue: function (dataView, dataOffset) {
31220 return String.fromCharCode(dataView.getUint8(dataOffset));
31225 // short, 16 bit int:
31227 getValue: function (dataView, dataOffset, littleEndian) {
31228 return dataView.getUint16(dataOffset, littleEndian);
31232 // long, 32 bit int:
31234 getValue: function (dataView, dataOffset, littleEndian) {
31235 return dataView.getUint32(dataOffset, littleEndian);
31239 // rational = two long values, first is numerator, second is denominator:
31241 getValue: function (dataView, dataOffset, littleEndian) {
31242 return dataView.getUint32(dataOffset, littleEndian) /
31243 dataView.getUint32(dataOffset + 4, littleEndian);
31247 // slong, 32 bit signed int:
31249 getValue: function (dataView, dataOffset, littleEndian) {
31250 return dataView.getInt32(dataOffset, littleEndian);
31254 // srational, two slongs, first is numerator, second is denominator:
31256 getValue: function (dataView, dataOffset, littleEndian) {
31257 return dataView.getInt32(dataOffset, littleEndian) /
31258 dataView.getInt32(dataOffset + 4, littleEndian);
31268 cls : 'btn-group roo-upload-cropbox-rotate-left',
31269 action : 'rotate-left',
31273 cls : 'btn btn-default',
31274 html : '<i class="fa fa-undo"></i>'
31280 cls : 'btn-group roo-upload-cropbox-picture',
31281 action : 'picture',
31285 cls : 'btn btn-default',
31286 html : '<i class="fa fa-picture-o"></i>'
31292 cls : 'btn-group roo-upload-cropbox-rotate-right',
31293 action : 'rotate-right',
31297 cls : 'btn btn-default',
31298 html : '<i class="fa fa-repeat"></i>'
31306 cls : 'btn-group roo-upload-cropbox-rotate-left',
31307 action : 'rotate-left',
31311 cls : 'btn btn-default',
31312 html : '<i class="fa fa-undo"></i>'
31318 cls : 'btn-group roo-upload-cropbox-download',
31319 action : 'download',
31323 cls : 'btn btn-default',
31324 html : '<i class="fa fa-download"></i>'
31330 cls : 'btn-group roo-upload-cropbox-crop',
31335 cls : 'btn btn-default',
31336 html : '<i class="fa fa-crop"></i>'
31342 cls : 'btn-group roo-upload-cropbox-trash',
31347 cls : 'btn btn-default',
31348 html : '<i class="fa fa-trash"></i>'
31354 cls : 'btn-group roo-upload-cropbox-rotate-right',
31355 action : 'rotate-right',
31359 cls : 'btn btn-default',
31360 html : '<i class="fa fa-repeat"></i>'
31368 cls : 'btn-group roo-upload-cropbox-rotate-left',
31369 action : 'rotate-left',
31373 cls : 'btn btn-default',
31374 html : '<i class="fa fa-undo"></i>'
31380 cls : 'btn-group roo-upload-cropbox-rotate-right',
31381 action : 'rotate-right',
31385 cls : 'btn btn-default',
31386 html : '<i class="fa fa-repeat"></i>'
31399 * @class Roo.bootstrap.DocumentManager
31400 * @extends Roo.bootstrap.Component
31401 * Bootstrap DocumentManager class
31402 * @cfg {String} paramName default 'imageUpload'
31403 * @cfg {String} toolTipName default 'filename'
31404 * @cfg {String} method default POST
31405 * @cfg {String} url action url
31406 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31407 * @cfg {Boolean} multiple multiple upload default true
31408 * @cfg {Number} thumbSize default 300
31409 * @cfg {String} fieldLabel
31410 * @cfg {Number} labelWidth default 4
31411 * @cfg {String} labelAlign (left|top) default left
31412 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31413 * @cfg {Number} labellg set the width of label (1-12)
31414 * @cfg {Number} labelmd set the width of label (1-12)
31415 * @cfg {Number} labelsm set the width of label (1-12)
31416 * @cfg {Number} labelxs set the width of label (1-12)
31419 * Create a new DocumentManager
31420 * @param {Object} config The config object
31423 Roo.bootstrap.DocumentManager = function(config){
31424 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31427 this.delegates = [];
31432 * Fire when initial the DocumentManager
31433 * @param {Roo.bootstrap.DocumentManager} this
31438 * inspect selected file
31439 * @param {Roo.bootstrap.DocumentManager} this
31440 * @param {File} file
31445 * Fire when xhr load exception
31446 * @param {Roo.bootstrap.DocumentManager} this
31447 * @param {XMLHttpRequest} xhr
31449 "exception" : true,
31451 * @event afterupload
31452 * Fire when xhr load exception
31453 * @param {Roo.bootstrap.DocumentManager} this
31454 * @param {XMLHttpRequest} xhr
31456 "afterupload" : true,
31459 * prepare the form data
31460 * @param {Roo.bootstrap.DocumentManager} this
31461 * @param {Object} formData
31466 * Fire when remove the file
31467 * @param {Roo.bootstrap.DocumentManager} this
31468 * @param {Object} file
31473 * Fire after refresh the file
31474 * @param {Roo.bootstrap.DocumentManager} this
31479 * Fire after click the image
31480 * @param {Roo.bootstrap.DocumentManager} this
31481 * @param {Object} file
31486 * Fire when upload a image and editable set to true
31487 * @param {Roo.bootstrap.DocumentManager} this
31488 * @param {Object} file
31492 * @event beforeselectfile
31493 * Fire before select file
31494 * @param {Roo.bootstrap.DocumentManager} this
31496 "beforeselectfile" : true,
31499 * Fire before process file
31500 * @param {Roo.bootstrap.DocumentManager} this
31501 * @param {Object} file
31505 * @event previewrendered
31506 * Fire when preview rendered
31507 * @param {Roo.bootstrap.DocumentManager} this
31508 * @param {Object} file
31510 "previewrendered" : true,
31513 "previewResize" : true
31518 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31527 paramName : 'imageUpload',
31528 toolTipName : 'filename',
31531 labelAlign : 'left',
31541 getAutoCreate : function()
31543 var managerWidget = {
31545 cls : 'roo-document-manager',
31549 cls : 'roo-document-manager-selector',
31554 cls : 'roo-document-manager-uploader',
31558 cls : 'roo-document-manager-upload-btn',
31559 html : '<i class="fa fa-plus"></i>'
31570 cls : 'column col-md-12',
31575 if(this.fieldLabel.length){
31580 cls : 'column col-md-12',
31581 html : this.fieldLabel
31585 cls : 'column col-md-12',
31590 if(this.labelAlign == 'left'){
31595 html : this.fieldLabel
31604 if(this.labelWidth > 12){
31605 content[0].style = "width: " + this.labelWidth + 'px';
31608 if(this.labelWidth < 13 && this.labelmd == 0){
31609 this.labelmd = this.labelWidth;
31612 if(this.labellg > 0){
31613 content[0].cls += ' col-lg-' + this.labellg;
31614 content[1].cls += ' col-lg-' + (12 - this.labellg);
31617 if(this.labelmd > 0){
31618 content[0].cls += ' col-md-' + this.labelmd;
31619 content[1].cls += ' col-md-' + (12 - this.labelmd);
31622 if(this.labelsm > 0){
31623 content[0].cls += ' col-sm-' + this.labelsm;
31624 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31627 if(this.labelxs > 0){
31628 content[0].cls += ' col-xs-' + this.labelxs;
31629 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31637 cls : 'row clearfix',
31645 initEvents : function()
31647 this.managerEl = this.el.select('.roo-document-manager', true).first();
31648 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31650 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31651 this.selectorEl.hide();
31654 this.selectorEl.attr('multiple', 'multiple');
31657 this.selectorEl.on('change', this.onFileSelected, this);
31659 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31660 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31662 this.uploader.on('click', this.onUploaderClick, this);
31664 this.renderProgressDialog();
31668 window.addEventListener("resize", function() { _this.refresh(); } );
31670 this.fireEvent('initial', this);
31673 renderProgressDialog : function()
31677 this.progressDialog = new Roo.bootstrap.Modal({
31678 cls : 'roo-document-manager-progress-dialog',
31679 allow_close : false,
31690 btnclick : function() {
31691 _this.uploadCancel();
31697 this.progressDialog.render(Roo.get(document.body));
31699 this.progress = new Roo.bootstrap.Progress({
31700 cls : 'roo-document-manager-progress',
31705 this.progress.render(this.progressDialog.getChildContainer());
31707 this.progressBar = new Roo.bootstrap.ProgressBar({
31708 cls : 'roo-document-manager-progress-bar',
31711 aria_valuemax : 12,
31715 this.progressBar.render(this.progress.getChildContainer());
31718 onUploaderClick : function(e)
31720 e.preventDefault();
31722 if(this.fireEvent('beforeselectfile', this) != false){
31723 this.selectorEl.dom.click();
31728 onFileSelected : function(e)
31730 e.preventDefault();
31732 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31736 Roo.each(this.selectorEl.dom.files, function(file){
31737 if(this.fireEvent('inspect', this, file) != false){
31738 this.files.push(file);
31748 this.selectorEl.dom.value = '';
31750 if(!this.files || !this.files.length){
31754 if(this.boxes > 0 && this.files.length > this.boxes){
31755 this.files = this.files.slice(0, this.boxes);
31758 this.uploader.show();
31760 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31761 this.uploader.hide();
31770 Roo.each(this.files, function(file){
31772 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31773 var f = this.renderPreview(file);
31778 if(file.type.indexOf('image') != -1){
31779 this.delegates.push(
31781 _this.process(file);
31782 }).createDelegate(this)
31790 _this.process(file);
31791 }).createDelegate(this)
31796 this.files = files;
31798 this.delegates = this.delegates.concat(docs);
31800 if(!this.delegates.length){
31805 this.progressBar.aria_valuemax = this.delegates.length;
31812 arrange : function()
31814 if(!this.delegates.length){
31815 this.progressDialog.hide();
31820 var delegate = this.delegates.shift();
31822 this.progressDialog.show();
31824 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31826 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31831 refresh : function()
31833 this.uploader.show();
31835 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31836 this.uploader.hide();
31839 Roo.isTouch ? this.closable(false) : this.closable(true);
31841 this.fireEvent('refresh', this);
31844 onRemove : function(e, el, o)
31846 e.preventDefault();
31848 this.fireEvent('remove', this, o);
31852 remove : function(o)
31856 Roo.each(this.files, function(file){
31857 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31866 this.files = files;
31873 Roo.each(this.files, function(file){
31878 file.target.remove();
31887 onClick : function(e, el, o)
31889 e.preventDefault();
31891 this.fireEvent('click', this, o);
31895 closable : function(closable)
31897 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31899 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31911 xhrOnLoad : function(xhr)
31913 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31917 if (xhr.readyState !== 4) {
31919 this.fireEvent('exception', this, xhr);
31923 var response = Roo.decode(xhr.responseText);
31925 if(!response.success){
31927 this.fireEvent('exception', this, xhr);
31931 var file = this.renderPreview(response.data);
31933 this.files.push(file);
31937 this.fireEvent('afterupload', this, xhr);
31941 xhrOnError : function(xhr)
31943 Roo.log('xhr on error');
31945 var response = Roo.decode(xhr.responseText);
31952 process : function(file)
31954 if(this.fireEvent('process', this, file) !== false){
31955 if(this.editable && file.type.indexOf('image') != -1){
31956 this.fireEvent('edit', this, file);
31960 this.uploadStart(file, false);
31967 uploadStart : function(file, crop)
31969 this.xhr = new XMLHttpRequest();
31971 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31976 file.xhr = this.xhr;
31978 this.managerEl.createChild({
31980 cls : 'roo-document-manager-loading',
31984 tooltip : file.name,
31985 cls : 'roo-document-manager-thumb',
31986 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31992 this.xhr.open(this.method, this.url, true);
31995 "Accept": "application/json",
31996 "Cache-Control": "no-cache",
31997 "X-Requested-With": "XMLHttpRequest"
32000 for (var headerName in headers) {
32001 var headerValue = headers[headerName];
32003 this.xhr.setRequestHeader(headerName, headerValue);
32009 this.xhr.onload = function()
32011 _this.xhrOnLoad(_this.xhr);
32014 this.xhr.onerror = function()
32016 _this.xhrOnError(_this.xhr);
32019 var formData = new FormData();
32021 formData.append('returnHTML', 'NO');
32024 formData.append('crop', crop);
32027 formData.append(this.paramName, file, file.name);
32034 if(this.fireEvent('prepare', this, formData, options) != false){
32036 if(options.manually){
32040 this.xhr.send(formData);
32044 this.uploadCancel();
32047 uploadCancel : function()
32053 this.delegates = [];
32055 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32062 renderPreview : function(file)
32064 if(typeof(file.target) != 'undefined' && file.target){
32068 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32070 var previewEl = this.managerEl.createChild({
32072 cls : 'roo-document-manager-preview',
32076 tooltip : file[this.toolTipName],
32077 cls : 'roo-document-manager-thumb',
32078 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32083 html : '<i class="fa fa-times-circle"></i>'
32088 var close = previewEl.select('button.close', true).first();
32090 close.on('click', this.onRemove, this, file);
32092 file.target = previewEl;
32094 var image = previewEl.select('img', true).first();
32098 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32100 image.on('click', this.onClick, this, file);
32102 this.fireEvent('previewrendered', this, file);
32108 onPreviewLoad : function(file, image)
32110 if(typeof(file.target) == 'undefined' || !file.target){
32114 var width = image.dom.naturalWidth || image.dom.width;
32115 var height = image.dom.naturalHeight || image.dom.height;
32117 if(!this.previewResize) {
32121 if(width > height){
32122 file.target.addClass('wide');
32126 file.target.addClass('tall');
32131 uploadFromSource : function(file, crop)
32133 this.xhr = new XMLHttpRequest();
32135 this.managerEl.createChild({
32137 cls : 'roo-document-manager-loading',
32141 tooltip : file.name,
32142 cls : 'roo-document-manager-thumb',
32143 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32149 this.xhr.open(this.method, this.url, true);
32152 "Accept": "application/json",
32153 "Cache-Control": "no-cache",
32154 "X-Requested-With": "XMLHttpRequest"
32157 for (var headerName in headers) {
32158 var headerValue = headers[headerName];
32160 this.xhr.setRequestHeader(headerName, headerValue);
32166 this.xhr.onload = function()
32168 _this.xhrOnLoad(_this.xhr);
32171 this.xhr.onerror = function()
32173 _this.xhrOnError(_this.xhr);
32176 var formData = new FormData();
32178 formData.append('returnHTML', 'NO');
32180 formData.append('crop', crop);
32182 if(typeof(file.filename) != 'undefined'){
32183 formData.append('filename', file.filename);
32186 if(typeof(file.mimetype) != 'undefined'){
32187 formData.append('mimetype', file.mimetype);
32192 if(this.fireEvent('prepare', this, formData) != false){
32193 this.xhr.send(formData);
32203 * @class Roo.bootstrap.DocumentViewer
32204 * @extends Roo.bootstrap.Component
32205 * Bootstrap DocumentViewer class
32206 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32207 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32210 * Create a new DocumentViewer
32211 * @param {Object} config The config object
32214 Roo.bootstrap.DocumentViewer = function(config){
32215 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32220 * Fire after initEvent
32221 * @param {Roo.bootstrap.DocumentViewer} this
32227 * @param {Roo.bootstrap.DocumentViewer} this
32232 * Fire after download button
32233 * @param {Roo.bootstrap.DocumentViewer} this
32238 * Fire after trash button
32239 * @param {Roo.bootstrap.DocumentViewer} this
32246 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32248 showDownload : true,
32252 getAutoCreate : function()
32256 cls : 'roo-document-viewer',
32260 cls : 'roo-document-viewer-body',
32264 cls : 'roo-document-viewer-thumb',
32268 cls : 'roo-document-viewer-image'
32276 cls : 'roo-document-viewer-footer',
32279 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32283 cls : 'btn-group roo-document-viewer-download',
32287 cls : 'btn btn-default',
32288 html : '<i class="fa fa-download"></i>'
32294 cls : 'btn-group roo-document-viewer-trash',
32298 cls : 'btn btn-default',
32299 html : '<i class="fa fa-trash"></i>'
32312 initEvents : function()
32314 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32315 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32317 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32318 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32320 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32321 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32323 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32324 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32326 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32327 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32329 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32330 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32332 this.bodyEl.on('click', this.onClick, this);
32333 this.downloadBtn.on('click', this.onDownload, this);
32334 this.trashBtn.on('click', this.onTrash, this);
32336 this.downloadBtn.hide();
32337 this.trashBtn.hide();
32339 if(this.showDownload){
32340 this.downloadBtn.show();
32343 if(this.showTrash){
32344 this.trashBtn.show();
32347 if(!this.showDownload && !this.showTrash) {
32348 this.footerEl.hide();
32353 initial : function()
32355 this.fireEvent('initial', this);
32359 onClick : function(e)
32361 e.preventDefault();
32363 this.fireEvent('click', this);
32366 onDownload : function(e)
32368 e.preventDefault();
32370 this.fireEvent('download', this);
32373 onTrash : function(e)
32375 e.preventDefault();
32377 this.fireEvent('trash', this);
32389 * @class Roo.bootstrap.NavProgressBar
32390 * @extends Roo.bootstrap.Component
32391 * Bootstrap NavProgressBar class
32394 * Create a new nav progress bar
32395 * @param {Object} config The config object
32398 Roo.bootstrap.NavProgressBar = function(config){
32399 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32401 this.bullets = this.bullets || [];
32403 // Roo.bootstrap.NavProgressBar.register(this);
32407 * Fires when the active item changes
32408 * @param {Roo.bootstrap.NavProgressBar} this
32409 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32410 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32417 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32422 getAutoCreate : function()
32424 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32428 cls : 'roo-navigation-bar-group',
32432 cls : 'roo-navigation-top-bar'
32436 cls : 'roo-navigation-bullets-bar',
32440 cls : 'roo-navigation-bar'
32447 cls : 'roo-navigation-bottom-bar'
32457 initEvents: function()
32462 onRender : function(ct, position)
32464 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32466 if(this.bullets.length){
32467 Roo.each(this.bullets, function(b){
32476 addItem : function(cfg)
32478 var item = new Roo.bootstrap.NavProgressItem(cfg);
32480 item.parentId = this.id;
32481 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32484 var top = new Roo.bootstrap.Element({
32486 cls : 'roo-navigation-bar-text'
32489 var bottom = new Roo.bootstrap.Element({
32491 cls : 'roo-navigation-bar-text'
32494 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32495 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32497 var topText = new Roo.bootstrap.Element({
32499 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32502 var bottomText = new Roo.bootstrap.Element({
32504 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32507 topText.onRender(top.el, null);
32508 bottomText.onRender(bottom.el, null);
32511 item.bottomEl = bottom;
32514 this.barItems.push(item);
32519 getActive : function()
32521 var active = false;
32523 Roo.each(this.barItems, function(v){
32525 if (!v.isActive()) {
32537 setActiveItem : function(item)
32541 Roo.each(this.barItems, function(v){
32542 if (v.rid == item.rid) {
32546 if (v.isActive()) {
32547 v.setActive(false);
32552 item.setActive(true);
32554 this.fireEvent('changed', this, item, prev);
32557 getBarItem: function(rid)
32561 Roo.each(this.barItems, function(e) {
32562 if (e.rid != rid) {
32573 indexOfItem : function(item)
32577 Roo.each(this.barItems, function(v, i){
32579 if (v.rid != item.rid) {
32590 setActiveNext : function()
32592 var i = this.indexOfItem(this.getActive());
32594 if (i > this.barItems.length) {
32598 this.setActiveItem(this.barItems[i+1]);
32601 setActivePrev : function()
32603 var i = this.indexOfItem(this.getActive());
32609 this.setActiveItem(this.barItems[i-1]);
32612 format : function()
32614 if(!this.barItems.length){
32618 var width = 100 / this.barItems.length;
32620 Roo.each(this.barItems, function(i){
32621 i.el.setStyle('width', width + '%');
32622 i.topEl.el.setStyle('width', width + '%');
32623 i.bottomEl.el.setStyle('width', width + '%');
32632 * Nav Progress Item
32637 * @class Roo.bootstrap.NavProgressItem
32638 * @extends Roo.bootstrap.Component
32639 * Bootstrap NavProgressItem class
32640 * @cfg {String} rid the reference id
32641 * @cfg {Boolean} active (true|false) Is item active default false
32642 * @cfg {Boolean} disabled (true|false) Is item active default false
32643 * @cfg {String} html
32644 * @cfg {String} position (top|bottom) text position default bottom
32645 * @cfg {String} icon show icon instead of number
32648 * Create a new NavProgressItem
32649 * @param {Object} config The config object
32651 Roo.bootstrap.NavProgressItem = function(config){
32652 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32657 * The raw click event for the entire grid.
32658 * @param {Roo.bootstrap.NavProgressItem} this
32659 * @param {Roo.EventObject} e
32666 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32672 position : 'bottom',
32675 getAutoCreate : function()
32677 var iconCls = 'roo-navigation-bar-item-icon';
32679 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32683 cls: 'roo-navigation-bar-item',
32693 cfg.cls += ' active';
32696 cfg.cls += ' disabled';
32702 disable : function()
32704 this.setDisabled(true);
32707 enable : function()
32709 this.setDisabled(false);
32712 initEvents: function()
32714 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32716 this.iconEl.on('click', this.onClick, this);
32719 onClick : function(e)
32721 e.preventDefault();
32727 if(this.fireEvent('click', this, e) === false){
32731 this.parent().setActiveItem(this);
32734 isActive: function ()
32736 return this.active;
32739 setActive : function(state)
32741 if(this.active == state){
32745 this.active = state;
32748 this.el.addClass('active');
32752 this.el.removeClass('active');
32757 setDisabled : function(state)
32759 if(this.disabled == state){
32763 this.disabled = state;
32766 this.el.addClass('disabled');
32770 this.el.removeClass('disabled');
32773 tooltipEl : function()
32775 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32788 * @class Roo.bootstrap.FieldLabel
32789 * @extends Roo.bootstrap.Component
32790 * Bootstrap FieldLabel class
32791 * @cfg {String} html contents of the element
32792 * @cfg {String} tag tag of the element default label
32793 * @cfg {String} cls class of the element
32794 * @cfg {String} target label target
32795 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32796 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32797 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32798 * @cfg {String} iconTooltip default "This field is required"
32799 * @cfg {String} indicatorpos (left|right) default left
32802 * Create a new FieldLabel
32803 * @param {Object} config The config object
32806 Roo.bootstrap.FieldLabel = function(config){
32807 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32812 * Fires after the field has been marked as invalid.
32813 * @param {Roo.form.FieldLabel} this
32814 * @param {String} msg The validation message
32819 * Fires after the field has been validated with no errors.
32820 * @param {Roo.form.FieldLabel} this
32826 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32833 invalidClass : 'has-warning',
32834 validClass : 'has-success',
32835 iconTooltip : 'This field is required',
32836 indicatorpos : 'left',
32838 getAutoCreate : function(){
32841 if (!this.allowBlank) {
32847 cls : 'roo-bootstrap-field-label ' + this.cls,
32852 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32853 tooltip : this.iconTooltip
32862 if(this.indicatorpos == 'right'){
32865 cls : 'roo-bootstrap-field-label ' + this.cls,
32874 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32875 tooltip : this.iconTooltip
32884 initEvents: function()
32886 Roo.bootstrap.Element.superclass.initEvents.call(this);
32888 this.indicator = this.indicatorEl();
32890 if(this.indicator){
32891 this.indicator.removeClass('visible');
32892 this.indicator.addClass('invisible');
32895 Roo.bootstrap.FieldLabel.register(this);
32898 indicatorEl : function()
32900 var indicator = this.el.select('i.roo-required-indicator',true).first();
32911 * Mark this field as valid
32913 markValid : function()
32915 if(this.indicator){
32916 this.indicator.removeClass('visible');
32917 this.indicator.addClass('invisible');
32919 if (Roo.bootstrap.version == 3) {
32920 this.el.removeClass(this.invalidClass);
32921 this.el.addClass(this.validClass);
32923 this.el.removeClass('is-invalid');
32924 this.el.addClass('is-valid');
32928 this.fireEvent('valid', this);
32932 * Mark this field as invalid
32933 * @param {String} msg The validation message
32935 markInvalid : function(msg)
32937 if(this.indicator){
32938 this.indicator.removeClass('invisible');
32939 this.indicator.addClass('visible');
32941 if (Roo.bootstrap.version == 3) {
32942 this.el.removeClass(this.validClass);
32943 this.el.addClass(this.invalidClass);
32945 this.el.removeClass('is-valid');
32946 this.el.addClass('is-invalid');
32950 this.fireEvent('invalid', this, msg);
32956 Roo.apply(Roo.bootstrap.FieldLabel, {
32961 * register a FieldLabel Group
32962 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32964 register : function(label)
32966 if(this.groups.hasOwnProperty(label.target)){
32970 this.groups[label.target] = label;
32974 * fetch a FieldLabel Group based on the target
32975 * @param {string} target
32976 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32978 get: function(target) {
32979 if (typeof(this.groups[target]) == 'undefined') {
32983 return this.groups[target] ;
32992 * page DateSplitField.
32998 * @class Roo.bootstrap.DateSplitField
32999 * @extends Roo.bootstrap.Component
33000 * Bootstrap DateSplitField class
33001 * @cfg {string} fieldLabel - the label associated
33002 * @cfg {Number} labelWidth set the width of label (0-12)
33003 * @cfg {String} labelAlign (top|left)
33004 * @cfg {Boolean} dayAllowBlank (true|false) default false
33005 * @cfg {Boolean} monthAllowBlank (true|false) default false
33006 * @cfg {Boolean} yearAllowBlank (true|false) default false
33007 * @cfg {string} dayPlaceholder
33008 * @cfg {string} monthPlaceholder
33009 * @cfg {string} yearPlaceholder
33010 * @cfg {string} dayFormat default 'd'
33011 * @cfg {string} monthFormat default 'm'
33012 * @cfg {string} yearFormat default 'Y'
33013 * @cfg {Number} labellg set the width of label (1-12)
33014 * @cfg {Number} labelmd set the width of label (1-12)
33015 * @cfg {Number} labelsm set the width of label (1-12)
33016 * @cfg {Number} labelxs set the width of label (1-12)
33020 * Create a new DateSplitField
33021 * @param {Object} config The config object
33024 Roo.bootstrap.DateSplitField = function(config){
33025 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33031 * getting the data of years
33032 * @param {Roo.bootstrap.DateSplitField} this
33033 * @param {Object} years
33038 * getting the data of days
33039 * @param {Roo.bootstrap.DateSplitField} this
33040 * @param {Object} days
33045 * Fires after the field has been marked as invalid.
33046 * @param {Roo.form.Field} this
33047 * @param {String} msg The validation message
33052 * Fires after the field has been validated with no errors.
33053 * @param {Roo.form.Field} this
33059 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33062 labelAlign : 'top',
33064 dayAllowBlank : false,
33065 monthAllowBlank : false,
33066 yearAllowBlank : false,
33067 dayPlaceholder : '',
33068 monthPlaceholder : '',
33069 yearPlaceholder : '',
33073 isFormField : true,
33079 getAutoCreate : function()
33083 cls : 'row roo-date-split-field-group',
33088 cls : 'form-hidden-field roo-date-split-field-group-value',
33094 var labelCls = 'col-md-12';
33095 var contentCls = 'col-md-4';
33097 if(this.fieldLabel){
33101 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33105 html : this.fieldLabel
33110 if(this.labelAlign == 'left'){
33112 if(this.labelWidth > 12){
33113 label.style = "width: " + this.labelWidth + 'px';
33116 if(this.labelWidth < 13 && this.labelmd == 0){
33117 this.labelmd = this.labelWidth;
33120 if(this.labellg > 0){
33121 labelCls = ' col-lg-' + this.labellg;
33122 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33125 if(this.labelmd > 0){
33126 labelCls = ' col-md-' + this.labelmd;
33127 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33130 if(this.labelsm > 0){
33131 labelCls = ' col-sm-' + this.labelsm;
33132 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33135 if(this.labelxs > 0){
33136 labelCls = ' col-xs-' + this.labelxs;
33137 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33141 label.cls += ' ' + labelCls;
33143 cfg.cn.push(label);
33146 Roo.each(['day', 'month', 'year'], function(t){
33149 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33156 inputEl: function ()
33158 return this.el.select('.roo-date-split-field-group-value', true).first();
33161 onRender : function(ct, position)
33165 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33167 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33169 this.dayField = new Roo.bootstrap.ComboBox({
33170 allowBlank : this.dayAllowBlank,
33171 alwaysQuery : true,
33172 displayField : 'value',
33175 forceSelection : true,
33177 placeholder : this.dayPlaceholder,
33178 selectOnFocus : true,
33179 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33180 triggerAction : 'all',
33182 valueField : 'value',
33183 store : new Roo.data.SimpleStore({
33184 data : (function() {
33186 _this.fireEvent('days', _this, days);
33189 fields : [ 'value' ]
33192 select : function (_self, record, index)
33194 _this.setValue(_this.getValue());
33199 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33201 this.monthField = new Roo.bootstrap.MonthField({
33202 after : '<i class=\"fa fa-calendar\"></i>',
33203 allowBlank : this.monthAllowBlank,
33204 placeholder : this.monthPlaceholder,
33207 render : function (_self)
33209 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33210 e.preventDefault();
33214 select : function (_self, oldvalue, newvalue)
33216 _this.setValue(_this.getValue());
33221 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33223 this.yearField = new Roo.bootstrap.ComboBox({
33224 allowBlank : this.yearAllowBlank,
33225 alwaysQuery : true,
33226 displayField : 'value',
33229 forceSelection : true,
33231 placeholder : this.yearPlaceholder,
33232 selectOnFocus : true,
33233 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33234 triggerAction : 'all',
33236 valueField : 'value',
33237 store : new Roo.data.SimpleStore({
33238 data : (function() {
33240 _this.fireEvent('years', _this, years);
33243 fields : [ 'value' ]
33246 select : function (_self, record, index)
33248 _this.setValue(_this.getValue());
33253 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33256 setValue : function(v, format)
33258 this.inputEl.dom.value = v;
33260 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33262 var d = Date.parseDate(v, f);
33269 this.setDay(d.format(this.dayFormat));
33270 this.setMonth(d.format(this.monthFormat));
33271 this.setYear(d.format(this.yearFormat));
33278 setDay : function(v)
33280 this.dayField.setValue(v);
33281 this.inputEl.dom.value = this.getValue();
33286 setMonth : function(v)
33288 this.monthField.setValue(v, true);
33289 this.inputEl.dom.value = this.getValue();
33294 setYear : function(v)
33296 this.yearField.setValue(v);
33297 this.inputEl.dom.value = this.getValue();
33302 getDay : function()
33304 return this.dayField.getValue();
33307 getMonth : function()
33309 return this.monthField.getValue();
33312 getYear : function()
33314 return this.yearField.getValue();
33317 getValue : function()
33319 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33321 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33331 this.inputEl.dom.value = '';
33336 validate : function()
33338 var d = this.dayField.validate();
33339 var m = this.monthField.validate();
33340 var y = this.yearField.validate();
33345 (!this.dayAllowBlank && !d) ||
33346 (!this.monthAllowBlank && !m) ||
33347 (!this.yearAllowBlank && !y)
33352 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33361 this.markInvalid();
33366 markValid : function()
33369 var label = this.el.select('label', true).first();
33370 var icon = this.el.select('i.fa-star', true).first();
33376 this.fireEvent('valid', this);
33380 * Mark this field as invalid
33381 * @param {String} msg The validation message
33383 markInvalid : function(msg)
33386 var label = this.el.select('label', true).first();
33387 var icon = this.el.select('i.fa-star', true).first();
33389 if(label && !icon){
33390 this.el.select('.roo-date-split-field-label', true).createChild({
33392 cls : 'text-danger fa fa-lg fa-star',
33393 tooltip : 'This field is required',
33394 style : 'margin-right:5px;'
33398 this.fireEvent('invalid', this, msg);
33401 clearInvalid : function()
33403 var label = this.el.select('label', true).first();
33404 var icon = this.el.select('i.fa-star', true).first();
33410 this.fireEvent('valid', this);
33413 getName: function()
33423 * http://masonry.desandro.com
33425 * The idea is to render all the bricks based on vertical width...
33427 * The original code extends 'outlayer' - we might need to use that....
33433 * @class Roo.bootstrap.LayoutMasonry
33434 * @extends Roo.bootstrap.Component
33435 * Bootstrap Layout Masonry class
33438 * Create a new Element
33439 * @param {Object} config The config object
33442 Roo.bootstrap.LayoutMasonry = function(config){
33444 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33448 Roo.bootstrap.LayoutMasonry.register(this);
33454 * Fire after layout the items
33455 * @param {Roo.bootstrap.LayoutMasonry} this
33456 * @param {Roo.EventObject} e
33463 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33466 * @cfg {Boolean} isLayoutInstant = no animation?
33468 isLayoutInstant : false, // needed?
33471 * @cfg {Number} boxWidth width of the columns
33476 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33481 * @cfg {Number} padWidth padding below box..
33486 * @cfg {Number} gutter gutter width..
33491 * @cfg {Number} maxCols maximum number of columns
33497 * @cfg {Boolean} isAutoInitial defalut true
33499 isAutoInitial : true,
33504 * @cfg {Boolean} isHorizontal defalut false
33506 isHorizontal : false,
33508 currentSize : null,
33514 bricks: null, //CompositeElement
33518 _isLayoutInited : false,
33520 // isAlternative : false, // only use for vertical layout...
33523 * @cfg {Number} alternativePadWidth padding below box..
33525 alternativePadWidth : 50,
33527 selectedBrick : [],
33529 getAutoCreate : function(){
33531 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33535 cls: 'blog-masonary-wrapper ' + this.cls,
33537 cls : 'mas-boxes masonary'
33544 getChildContainer: function( )
33546 if (this.boxesEl) {
33547 return this.boxesEl;
33550 this.boxesEl = this.el.select('.mas-boxes').first();
33552 return this.boxesEl;
33556 initEvents : function()
33560 if(this.isAutoInitial){
33561 Roo.log('hook children rendered');
33562 this.on('childrenrendered', function() {
33563 Roo.log('children rendered');
33569 initial : function()
33571 this.selectedBrick = [];
33573 this.currentSize = this.el.getBox(true);
33575 Roo.EventManager.onWindowResize(this.resize, this);
33577 if(!this.isAutoInitial){
33585 //this.layout.defer(500,this);
33589 resize : function()
33591 var cs = this.el.getBox(true);
33594 this.currentSize.width == cs.width &&
33595 this.currentSize.x == cs.x &&
33596 this.currentSize.height == cs.height &&
33597 this.currentSize.y == cs.y
33599 Roo.log("no change in with or X or Y");
33603 this.currentSize = cs;
33609 layout : function()
33611 this._resetLayout();
33613 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33615 this.layoutItems( isInstant );
33617 this._isLayoutInited = true;
33619 this.fireEvent('layout', this);
33623 _resetLayout : function()
33625 if(this.isHorizontal){
33626 this.horizontalMeasureColumns();
33630 this.verticalMeasureColumns();
33634 verticalMeasureColumns : function()
33636 this.getContainerWidth();
33638 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33639 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33643 var boxWidth = this.boxWidth + this.padWidth;
33645 if(this.containerWidth < this.boxWidth){
33646 boxWidth = this.containerWidth
33649 var containerWidth = this.containerWidth;
33651 var cols = Math.floor(containerWidth / boxWidth);
33653 this.cols = Math.max( cols, 1 );
33655 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33657 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33659 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33661 this.colWidth = boxWidth + avail - this.padWidth;
33663 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33664 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33667 horizontalMeasureColumns : function()
33669 this.getContainerWidth();
33671 var boxWidth = this.boxWidth;
33673 if(this.containerWidth < boxWidth){
33674 boxWidth = this.containerWidth;
33677 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33679 this.el.setHeight(boxWidth);
33683 getContainerWidth : function()
33685 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33688 layoutItems : function( isInstant )
33690 Roo.log(this.bricks);
33692 var items = Roo.apply([], this.bricks);
33694 if(this.isHorizontal){
33695 this._horizontalLayoutItems( items , isInstant );
33699 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33700 // this._verticalAlternativeLayoutItems( items , isInstant );
33704 this._verticalLayoutItems( items , isInstant );
33708 _verticalLayoutItems : function ( items , isInstant)
33710 if ( !items || !items.length ) {
33715 ['xs', 'xs', 'xs', 'tall'],
33716 ['xs', 'xs', 'tall'],
33717 ['xs', 'xs', 'sm'],
33718 ['xs', 'xs', 'xs'],
33724 ['sm', 'xs', 'xs'],
33728 ['tall', 'xs', 'xs', 'xs'],
33729 ['tall', 'xs', 'xs'],
33741 Roo.each(items, function(item, k){
33743 switch (item.size) {
33744 // these layouts take up a full box,
33755 boxes.push([item]);
33778 var filterPattern = function(box, length)
33786 var pattern = box.slice(0, length);
33790 Roo.each(pattern, function(i){
33791 format.push(i.size);
33794 Roo.each(standard, function(s){
33796 if(String(s) != String(format)){
33805 if(!match && length == 1){
33810 filterPattern(box, length - 1);
33814 queue.push(pattern);
33816 box = box.slice(length, box.length);
33818 filterPattern(box, 4);
33824 Roo.each(boxes, function(box, k){
33830 if(box.length == 1){
33835 filterPattern(box, 4);
33839 this._processVerticalLayoutQueue( queue, isInstant );
33843 // _verticalAlternativeLayoutItems : function( items , isInstant )
33845 // if ( !items || !items.length ) {
33849 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33853 _horizontalLayoutItems : function ( items , isInstant)
33855 if ( !items || !items.length || items.length < 3) {
33861 var eItems = items.slice(0, 3);
33863 items = items.slice(3, items.length);
33866 ['xs', 'xs', 'xs', 'wide'],
33867 ['xs', 'xs', 'wide'],
33868 ['xs', 'xs', 'sm'],
33869 ['xs', 'xs', 'xs'],
33875 ['sm', 'xs', 'xs'],
33879 ['wide', 'xs', 'xs', 'xs'],
33880 ['wide', 'xs', 'xs'],
33893 Roo.each(items, function(item, k){
33895 switch (item.size) {
33906 boxes.push([item]);
33930 var filterPattern = function(box, length)
33938 var pattern = box.slice(0, length);
33942 Roo.each(pattern, function(i){
33943 format.push(i.size);
33946 Roo.each(standard, function(s){
33948 if(String(s) != String(format)){
33957 if(!match && length == 1){
33962 filterPattern(box, length - 1);
33966 queue.push(pattern);
33968 box = box.slice(length, box.length);
33970 filterPattern(box, 4);
33976 Roo.each(boxes, function(box, k){
33982 if(box.length == 1){
33987 filterPattern(box, 4);
33994 var pos = this.el.getBox(true);
33998 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34000 var hit_end = false;
34002 Roo.each(queue, function(box){
34006 Roo.each(box, function(b){
34008 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34018 Roo.each(box, function(b){
34020 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34023 mx = Math.max(mx, b.x);
34027 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34031 Roo.each(box, function(b){
34033 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34047 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34050 /** Sets position of item in DOM
34051 * @param {Element} item
34052 * @param {Number} x - horizontal position
34053 * @param {Number} y - vertical position
34054 * @param {Boolean} isInstant - disables transitions
34056 _processVerticalLayoutQueue : function( queue, isInstant )
34058 var pos = this.el.getBox(true);
34063 for (var i = 0; i < this.cols; i++){
34067 Roo.each(queue, function(box, k){
34069 var col = k % this.cols;
34071 Roo.each(box, function(b,kk){
34073 b.el.position('absolute');
34075 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34076 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34078 if(b.size == 'md-left' || b.size == 'md-right'){
34079 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34080 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34083 b.el.setWidth(width);
34084 b.el.setHeight(height);
34086 b.el.select('iframe',true).setSize(width,height);
34090 for (var i = 0; i < this.cols; i++){
34092 if(maxY[i] < maxY[col]){
34097 col = Math.min(col, i);
34101 x = pos.x + col * (this.colWidth + this.padWidth);
34105 var positions = [];
34107 switch (box.length){
34109 positions = this.getVerticalOneBoxColPositions(x, y, box);
34112 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34115 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34118 positions = this.getVerticalFourBoxColPositions(x, y, box);
34124 Roo.each(box, function(b,kk){
34126 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34128 var sz = b.el.getSize();
34130 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34138 for (var i = 0; i < this.cols; i++){
34139 mY = Math.max(mY, maxY[i]);
34142 this.el.setHeight(mY - pos.y);
34146 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34148 // var pos = this.el.getBox(true);
34151 // var maxX = pos.right;
34153 // var maxHeight = 0;
34155 // Roo.each(items, function(item, k){
34159 // item.el.position('absolute');
34161 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34163 // item.el.setWidth(width);
34165 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34167 // item.el.setHeight(height);
34170 // item.el.setXY([x, y], isInstant ? false : true);
34172 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34175 // y = y + height + this.alternativePadWidth;
34177 // maxHeight = maxHeight + height + this.alternativePadWidth;
34181 // this.el.setHeight(maxHeight);
34185 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34187 var pos = this.el.getBox(true);
34192 var maxX = pos.right;
34194 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34196 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34198 Roo.each(queue, function(box, k){
34200 Roo.each(box, function(b, kk){
34202 b.el.position('absolute');
34204 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34205 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34207 if(b.size == 'md-left' || b.size == 'md-right'){
34208 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34209 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34212 b.el.setWidth(width);
34213 b.el.setHeight(height);
34221 var positions = [];
34223 switch (box.length){
34225 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34228 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34231 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34234 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34240 Roo.each(box, function(b,kk){
34242 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34244 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34252 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34254 Roo.each(eItems, function(b,k){
34256 b.size = (k == 0) ? 'sm' : 'xs';
34257 b.x = (k == 0) ? 2 : 1;
34258 b.y = (k == 0) ? 2 : 1;
34260 b.el.position('absolute');
34262 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34264 b.el.setWidth(width);
34266 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34268 b.el.setHeight(height);
34272 var positions = [];
34275 x : maxX - this.unitWidth * 2 - this.gutter,
34280 x : maxX - this.unitWidth,
34281 y : minY + (this.unitWidth + this.gutter) * 2
34285 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34289 Roo.each(eItems, function(b,k){
34291 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34297 getVerticalOneBoxColPositions : function(x, y, box)
34301 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34303 if(box[0].size == 'md-left'){
34307 if(box[0].size == 'md-right'){
34312 x : x + (this.unitWidth + this.gutter) * rand,
34319 getVerticalTwoBoxColPositions : function(x, y, box)
34323 if(box[0].size == 'xs'){
34327 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34331 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34345 x : x + (this.unitWidth + this.gutter) * 2,
34346 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34353 getVerticalThreeBoxColPositions : function(x, y, box)
34357 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34365 x : x + (this.unitWidth + this.gutter) * 1,
34370 x : x + (this.unitWidth + this.gutter) * 2,
34378 if(box[0].size == 'xs' && box[1].size == 'xs'){
34387 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34391 x : x + (this.unitWidth + this.gutter) * 1,
34405 x : x + (this.unitWidth + this.gutter) * 2,
34410 x : x + (this.unitWidth + this.gutter) * 2,
34411 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34418 getVerticalFourBoxColPositions : function(x, y, box)
34422 if(box[0].size == 'xs'){
34431 y : y + (this.unitHeight + this.gutter) * 1
34436 y : y + (this.unitHeight + this.gutter) * 2
34440 x : x + (this.unitWidth + this.gutter) * 1,
34454 x : x + (this.unitWidth + this.gutter) * 2,
34459 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34460 y : y + (this.unitHeight + this.gutter) * 1
34464 x : x + (this.unitWidth + this.gutter) * 2,
34465 y : y + (this.unitWidth + this.gutter) * 2
34472 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34476 if(box[0].size == 'md-left'){
34478 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34485 if(box[0].size == 'md-right'){
34487 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34488 y : minY + (this.unitWidth + this.gutter) * 1
34494 var rand = Math.floor(Math.random() * (4 - box[0].y));
34497 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34498 y : minY + (this.unitWidth + this.gutter) * rand
34505 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34509 if(box[0].size == 'xs'){
34512 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34517 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34518 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34526 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34531 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34532 y : minY + (this.unitWidth + this.gutter) * 2
34539 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34543 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34546 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34551 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34552 y : minY + (this.unitWidth + this.gutter) * 1
34556 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34557 y : minY + (this.unitWidth + this.gutter) * 2
34564 if(box[0].size == 'xs' && box[1].size == 'xs'){
34567 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34572 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34577 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34578 y : minY + (this.unitWidth + this.gutter) * 1
34586 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34591 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34592 y : minY + (this.unitWidth + this.gutter) * 2
34596 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34597 y : minY + (this.unitWidth + this.gutter) * 2
34604 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34608 if(box[0].size == 'xs'){
34611 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34616 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34621 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),
34626 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34627 y : minY + (this.unitWidth + this.gutter) * 1
34635 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34640 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34641 y : minY + (this.unitWidth + this.gutter) * 2
34645 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34646 y : minY + (this.unitWidth + this.gutter) * 2
34650 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),
34651 y : minY + (this.unitWidth + this.gutter) * 2
34659 * remove a Masonry Brick
34660 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34662 removeBrick : function(brick_id)
34668 for (var i = 0; i<this.bricks.length; i++) {
34669 if (this.bricks[i].id == brick_id) {
34670 this.bricks.splice(i,1);
34671 this.el.dom.removeChild(Roo.get(brick_id).dom);
34678 * adds a Masonry Brick
34679 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34681 addBrick : function(cfg)
34683 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34684 //this.register(cn);
34685 cn.parentId = this.id;
34686 cn.render(this.el);
34691 * register a Masonry Brick
34692 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34695 register : function(brick)
34697 this.bricks.push(brick);
34698 brick.masonryId = this.id;
34702 * clear all the Masonry Brick
34704 clearAll : function()
34707 //this.getChildContainer().dom.innerHTML = "";
34708 this.el.dom.innerHTML = '';
34711 getSelected : function()
34713 if (!this.selectedBrick) {
34717 return this.selectedBrick;
34721 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34725 * register a Masonry Layout
34726 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34729 register : function(layout)
34731 this.groups[layout.id] = layout;
34734 * fetch a Masonry Layout based on the masonry layout ID
34735 * @param {string} the masonry layout to add
34736 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34739 get: function(layout_id) {
34740 if (typeof(this.groups[layout_id]) == 'undefined') {
34743 return this.groups[layout_id] ;
34755 * http://masonry.desandro.com
34757 * The idea is to render all the bricks based on vertical width...
34759 * The original code extends 'outlayer' - we might need to use that....
34765 * @class Roo.bootstrap.LayoutMasonryAuto
34766 * @extends Roo.bootstrap.Component
34767 * Bootstrap Layout Masonry class
34770 * Create a new Element
34771 * @param {Object} config The config object
34774 Roo.bootstrap.LayoutMasonryAuto = function(config){
34775 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34778 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34781 * @cfg {Boolean} isFitWidth - resize the width..
34783 isFitWidth : false, // options..
34785 * @cfg {Boolean} isOriginLeft = left align?
34787 isOriginLeft : true,
34789 * @cfg {Boolean} isOriginTop = top align?
34791 isOriginTop : false,
34793 * @cfg {Boolean} isLayoutInstant = no animation?
34795 isLayoutInstant : false, // needed?
34797 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34799 isResizingContainer : true,
34801 * @cfg {Number} columnWidth width of the columns
34807 * @cfg {Number} maxCols maximum number of columns
34812 * @cfg {Number} padHeight padding below box..
34818 * @cfg {Boolean} isAutoInitial defalut true
34821 isAutoInitial : true,
34827 initialColumnWidth : 0,
34828 currentSize : null,
34830 colYs : null, // array.
34837 bricks: null, //CompositeElement
34838 cols : 0, // array?
34839 // element : null, // wrapped now this.el
34840 _isLayoutInited : null,
34843 getAutoCreate : function(){
34847 cls: 'blog-masonary-wrapper ' + this.cls,
34849 cls : 'mas-boxes masonary'
34856 getChildContainer: function( )
34858 if (this.boxesEl) {
34859 return this.boxesEl;
34862 this.boxesEl = this.el.select('.mas-boxes').first();
34864 return this.boxesEl;
34868 initEvents : function()
34872 if(this.isAutoInitial){
34873 Roo.log('hook children rendered');
34874 this.on('childrenrendered', function() {
34875 Roo.log('children rendered');
34882 initial : function()
34884 this.reloadItems();
34886 this.currentSize = this.el.getBox(true);
34888 /// was window resize... - let's see if this works..
34889 Roo.EventManager.onWindowResize(this.resize, this);
34891 if(!this.isAutoInitial){
34896 this.layout.defer(500,this);
34899 reloadItems: function()
34901 this.bricks = this.el.select('.masonry-brick', true);
34903 this.bricks.each(function(b) {
34904 //Roo.log(b.getSize());
34905 if (!b.attr('originalwidth')) {
34906 b.attr('originalwidth', b.getSize().width);
34911 Roo.log(this.bricks.elements.length);
34914 resize : function()
34917 var cs = this.el.getBox(true);
34919 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34920 Roo.log("no change in with or X");
34923 this.currentSize = cs;
34927 layout : function()
34930 this._resetLayout();
34931 //this._manageStamps();
34933 // don't animate first layout
34934 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34935 this.layoutItems( isInstant );
34937 // flag for initalized
34938 this._isLayoutInited = true;
34941 layoutItems : function( isInstant )
34943 //var items = this._getItemsForLayout( this.items );
34944 // original code supports filtering layout items.. we just ignore it..
34946 this._layoutItems( this.bricks , isInstant );
34948 this._postLayout();
34950 _layoutItems : function ( items , isInstant)
34952 //this.fireEvent( 'layout', this, items );
34955 if ( !items || !items.elements.length ) {
34956 // no items, emit event with empty array
34961 items.each(function(item) {
34962 Roo.log("layout item");
34964 // get x/y object from method
34965 var position = this._getItemLayoutPosition( item );
34967 position.item = item;
34968 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34969 queue.push( position );
34972 this._processLayoutQueue( queue );
34974 /** Sets position of item in DOM
34975 * @param {Element} item
34976 * @param {Number} x - horizontal position
34977 * @param {Number} y - vertical position
34978 * @param {Boolean} isInstant - disables transitions
34980 _processLayoutQueue : function( queue )
34982 for ( var i=0, len = queue.length; i < len; i++ ) {
34983 var obj = queue[i];
34984 obj.item.position('absolute');
34985 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34991 * Any logic you want to do after each layout,
34992 * i.e. size the container
34994 _postLayout : function()
34996 this.resizeContainer();
34999 resizeContainer : function()
35001 if ( !this.isResizingContainer ) {
35004 var size = this._getContainerSize();
35006 this.el.setSize(size.width,size.height);
35007 this.boxesEl.setSize(size.width,size.height);
35013 _resetLayout : function()
35015 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35016 this.colWidth = this.el.getWidth();
35017 //this.gutter = this.el.getWidth();
35019 this.measureColumns();
35025 this.colYs.push( 0 );
35031 measureColumns : function()
35033 this.getContainerWidth();
35034 // if columnWidth is 0, default to outerWidth of first item
35035 if ( !this.columnWidth ) {
35036 var firstItem = this.bricks.first();
35037 Roo.log(firstItem);
35038 this.columnWidth = this.containerWidth;
35039 if (firstItem && firstItem.attr('originalwidth') ) {
35040 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35042 // columnWidth fall back to item of first element
35043 Roo.log("set column width?");
35044 this.initialColumnWidth = this.columnWidth ;
35046 // if first elem has no width, default to size of container
35051 if (this.initialColumnWidth) {
35052 this.columnWidth = this.initialColumnWidth;
35057 // column width is fixed at the top - however if container width get's smaller we should
35060 // this bit calcs how man columns..
35062 var columnWidth = this.columnWidth += this.gutter;
35064 // calculate columns
35065 var containerWidth = this.containerWidth + this.gutter;
35067 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35068 // fix rounding errors, typically with gutters
35069 var excess = columnWidth - containerWidth % columnWidth;
35072 // if overshoot is less than a pixel, round up, otherwise floor it
35073 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35074 cols = Math[ mathMethod ]( cols );
35075 this.cols = Math.max( cols, 1 );
35076 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35078 // padding positioning..
35079 var totalColWidth = this.cols * this.columnWidth;
35080 var padavail = this.containerWidth - totalColWidth;
35081 // so for 2 columns - we need 3 'pads'
35083 var padNeeded = (1+this.cols) * this.padWidth;
35085 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35087 this.columnWidth += padExtra
35088 //this.padWidth = Math.floor(padavail / ( this.cols));
35090 // adjust colum width so that padding is fixed??
35092 // we have 3 columns ... total = width * 3
35093 // we have X left over... that should be used by
35095 //if (this.expandC) {
35103 getContainerWidth : function()
35105 /* // container is parent if fit width
35106 var container = this.isFitWidth ? this.element.parentNode : this.element;
35107 // check that this.size and size are there
35108 // IE8 triggers resize on body size change, so they might not be
35110 var size = getSize( container ); //FIXME
35111 this.containerWidth = size && size.innerWidth; //FIXME
35114 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35118 _getItemLayoutPosition : function( item ) // what is item?
35120 // we resize the item to our columnWidth..
35122 item.setWidth(this.columnWidth);
35123 item.autoBoxAdjust = false;
35125 var sz = item.getSize();
35127 // how many columns does this brick span
35128 var remainder = this.containerWidth % this.columnWidth;
35130 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35131 // round if off by 1 pixel, otherwise use ceil
35132 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35133 colSpan = Math.min( colSpan, this.cols );
35135 // normally this should be '1' as we dont' currently allow multi width columns..
35137 var colGroup = this._getColGroup( colSpan );
35138 // get the minimum Y value from the columns
35139 var minimumY = Math.min.apply( Math, colGroup );
35140 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35142 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35144 // position the brick
35146 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35147 y: this.currentSize.y + minimumY + this.padHeight
35151 // apply setHeight to necessary columns
35152 var setHeight = minimumY + sz.height + this.padHeight;
35153 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35155 var setSpan = this.cols + 1 - colGroup.length;
35156 for ( var i = 0; i < setSpan; i++ ) {
35157 this.colYs[ shortColIndex + i ] = setHeight ;
35164 * @param {Number} colSpan - number of columns the element spans
35165 * @returns {Array} colGroup
35167 _getColGroup : function( colSpan )
35169 if ( colSpan < 2 ) {
35170 // if brick spans only one column, use all the column Ys
35175 // how many different places could this brick fit horizontally
35176 var groupCount = this.cols + 1 - colSpan;
35177 // for each group potential horizontal position
35178 for ( var i = 0; i < groupCount; i++ ) {
35179 // make an array of colY values for that one group
35180 var groupColYs = this.colYs.slice( i, i + colSpan );
35181 // and get the max value of the array
35182 colGroup[i] = Math.max.apply( Math, groupColYs );
35187 _manageStamp : function( stamp )
35189 var stampSize = stamp.getSize();
35190 var offset = stamp.getBox();
35191 // get the columns that this stamp affects
35192 var firstX = this.isOriginLeft ? offset.x : offset.right;
35193 var lastX = firstX + stampSize.width;
35194 var firstCol = Math.floor( firstX / this.columnWidth );
35195 firstCol = Math.max( 0, firstCol );
35197 var lastCol = Math.floor( lastX / this.columnWidth );
35198 // lastCol should not go over if multiple of columnWidth #425
35199 lastCol -= lastX % this.columnWidth ? 0 : 1;
35200 lastCol = Math.min( this.cols - 1, lastCol );
35202 // set colYs to bottom of the stamp
35203 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35206 for ( var i = firstCol; i <= lastCol; i++ ) {
35207 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35212 _getContainerSize : function()
35214 this.maxY = Math.max.apply( Math, this.colYs );
35219 if ( this.isFitWidth ) {
35220 size.width = this._getContainerFitWidth();
35226 _getContainerFitWidth : function()
35228 var unusedCols = 0;
35229 // count unused columns
35232 if ( this.colYs[i] !== 0 ) {
35237 // fit container to columns that have been used
35238 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35241 needsResizeLayout : function()
35243 var previousWidth = this.containerWidth;
35244 this.getContainerWidth();
35245 return previousWidth !== this.containerWidth;
35260 * @class Roo.bootstrap.MasonryBrick
35261 * @extends Roo.bootstrap.Component
35262 * Bootstrap MasonryBrick class
35265 * Create a new MasonryBrick
35266 * @param {Object} config The config object
35269 Roo.bootstrap.MasonryBrick = function(config){
35271 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35273 Roo.bootstrap.MasonryBrick.register(this);
35279 * When a MasonryBrick is clcik
35280 * @param {Roo.bootstrap.MasonryBrick} this
35281 * @param {Roo.EventObject} e
35287 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35290 * @cfg {String} title
35294 * @cfg {String} html
35298 * @cfg {String} bgimage
35302 * @cfg {String} videourl
35306 * @cfg {String} cls
35310 * @cfg {String} href
35314 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35319 * @cfg {String} placetitle (center|bottom)
35324 * @cfg {Boolean} isFitContainer defalut true
35326 isFitContainer : true,
35329 * @cfg {Boolean} preventDefault defalut false
35331 preventDefault : false,
35334 * @cfg {Boolean} inverse defalut false
35336 maskInverse : false,
35338 getAutoCreate : function()
35340 if(!this.isFitContainer){
35341 return this.getSplitAutoCreate();
35344 var cls = 'masonry-brick masonry-brick-full';
35346 if(this.href.length){
35347 cls += ' masonry-brick-link';
35350 if(this.bgimage.length){
35351 cls += ' masonry-brick-image';
35354 if(this.maskInverse){
35355 cls += ' mask-inverse';
35358 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35359 cls += ' enable-mask';
35363 cls += ' masonry-' + this.size + '-brick';
35366 if(this.placetitle.length){
35368 switch (this.placetitle) {
35370 cls += ' masonry-center-title';
35373 cls += ' masonry-bottom-title';
35380 if(!this.html.length && !this.bgimage.length){
35381 cls += ' masonry-center-title';
35384 if(!this.html.length && this.bgimage.length){
35385 cls += ' masonry-bottom-title';
35390 cls += ' ' + this.cls;
35394 tag: (this.href.length) ? 'a' : 'div',
35399 cls: 'masonry-brick-mask'
35403 cls: 'masonry-brick-paragraph',
35409 if(this.href.length){
35410 cfg.href = this.href;
35413 var cn = cfg.cn[1].cn;
35415 if(this.title.length){
35418 cls: 'masonry-brick-title',
35423 if(this.html.length){
35426 cls: 'masonry-brick-text',
35431 if (!this.title.length && !this.html.length) {
35432 cfg.cn[1].cls += ' hide';
35435 if(this.bgimage.length){
35438 cls: 'masonry-brick-image-view',
35443 if(this.videourl.length){
35444 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35445 // youtube support only?
35448 cls: 'masonry-brick-image-view',
35451 allowfullscreen : true
35459 getSplitAutoCreate : function()
35461 var cls = 'masonry-brick masonry-brick-split';
35463 if(this.href.length){
35464 cls += ' masonry-brick-link';
35467 if(this.bgimage.length){
35468 cls += ' masonry-brick-image';
35472 cls += ' masonry-' + this.size + '-brick';
35475 switch (this.placetitle) {
35477 cls += ' masonry-center-title';
35480 cls += ' masonry-bottom-title';
35483 if(!this.bgimage.length){
35484 cls += ' masonry-center-title';
35487 if(this.bgimage.length){
35488 cls += ' masonry-bottom-title';
35494 cls += ' ' + this.cls;
35498 tag: (this.href.length) ? 'a' : 'div',
35503 cls: 'masonry-brick-split-head',
35507 cls: 'masonry-brick-paragraph',
35514 cls: 'masonry-brick-split-body',
35520 if(this.href.length){
35521 cfg.href = this.href;
35524 if(this.title.length){
35525 cfg.cn[0].cn[0].cn.push({
35527 cls: 'masonry-brick-title',
35532 if(this.html.length){
35533 cfg.cn[1].cn.push({
35535 cls: 'masonry-brick-text',
35540 if(this.bgimage.length){
35541 cfg.cn[0].cn.push({
35543 cls: 'masonry-brick-image-view',
35548 if(this.videourl.length){
35549 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35550 // youtube support only?
35551 cfg.cn[0].cn.cn.push({
35553 cls: 'masonry-brick-image-view',
35556 allowfullscreen : true
35563 initEvents: function()
35565 switch (this.size) {
35598 this.el.on('touchstart', this.onTouchStart, this);
35599 this.el.on('touchmove', this.onTouchMove, this);
35600 this.el.on('touchend', this.onTouchEnd, this);
35601 this.el.on('contextmenu', this.onContextMenu, this);
35603 this.el.on('mouseenter' ,this.enter, this);
35604 this.el.on('mouseleave', this.leave, this);
35605 this.el.on('click', this.onClick, this);
35608 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35609 this.parent().bricks.push(this);
35614 onClick: function(e, el)
35616 var time = this.endTimer - this.startTimer;
35617 // Roo.log(e.preventDefault());
35620 e.preventDefault();
35625 if(!this.preventDefault){
35629 e.preventDefault();
35631 if (this.activeClass != '') {
35632 this.selectBrick();
35635 this.fireEvent('click', this, e);
35638 enter: function(e, el)
35640 e.preventDefault();
35642 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35646 if(this.bgimage.length && this.html.length){
35647 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35651 leave: function(e, el)
35653 e.preventDefault();
35655 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35659 if(this.bgimage.length && this.html.length){
35660 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35664 onTouchStart: function(e, el)
35666 // e.preventDefault();
35668 this.touchmoved = false;
35670 if(!this.isFitContainer){
35674 if(!this.bgimage.length || !this.html.length){
35678 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35680 this.timer = new Date().getTime();
35684 onTouchMove: function(e, el)
35686 this.touchmoved = true;
35689 onContextMenu : function(e,el)
35691 e.preventDefault();
35692 e.stopPropagation();
35696 onTouchEnd: function(e, el)
35698 // e.preventDefault();
35700 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35707 if(!this.bgimage.length || !this.html.length){
35709 if(this.href.length){
35710 window.location.href = this.href;
35716 if(!this.isFitContainer){
35720 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35722 window.location.href = this.href;
35725 //selection on single brick only
35726 selectBrick : function() {
35728 if (!this.parentId) {
35732 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35733 var index = m.selectedBrick.indexOf(this.id);
35736 m.selectedBrick.splice(index,1);
35737 this.el.removeClass(this.activeClass);
35741 for(var i = 0; i < m.selectedBrick.length; i++) {
35742 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35743 b.el.removeClass(b.activeClass);
35746 m.selectedBrick = [];
35748 m.selectedBrick.push(this.id);
35749 this.el.addClass(this.activeClass);
35753 isSelected : function(){
35754 return this.el.hasClass(this.activeClass);
35759 Roo.apply(Roo.bootstrap.MasonryBrick, {
35762 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35764 * register a Masonry Brick
35765 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35768 register : function(brick)
35770 //this.groups[brick.id] = brick;
35771 this.groups.add(brick.id, brick);
35774 * fetch a masonry brick based on the masonry brick ID
35775 * @param {string} the masonry brick to add
35776 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35779 get: function(brick_id)
35781 // if (typeof(this.groups[brick_id]) == 'undefined') {
35784 // return this.groups[brick_id] ;
35786 if(this.groups.key(brick_id)) {
35787 return this.groups.key(brick_id);
35805 * @class Roo.bootstrap.Brick
35806 * @extends Roo.bootstrap.Component
35807 * Bootstrap Brick class
35810 * Create a new Brick
35811 * @param {Object} config The config object
35814 Roo.bootstrap.Brick = function(config){
35815 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35821 * When a Brick is click
35822 * @param {Roo.bootstrap.Brick} this
35823 * @param {Roo.EventObject} e
35829 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35832 * @cfg {String} title
35836 * @cfg {String} html
35840 * @cfg {String} bgimage
35844 * @cfg {String} cls
35848 * @cfg {String} href
35852 * @cfg {String} video
35856 * @cfg {Boolean} square
35860 getAutoCreate : function()
35862 var cls = 'roo-brick';
35864 if(this.href.length){
35865 cls += ' roo-brick-link';
35868 if(this.bgimage.length){
35869 cls += ' roo-brick-image';
35872 if(!this.html.length && !this.bgimage.length){
35873 cls += ' roo-brick-center-title';
35876 if(!this.html.length && this.bgimage.length){
35877 cls += ' roo-brick-bottom-title';
35881 cls += ' ' + this.cls;
35885 tag: (this.href.length) ? 'a' : 'div',
35890 cls: 'roo-brick-paragraph',
35896 if(this.href.length){
35897 cfg.href = this.href;
35900 var cn = cfg.cn[0].cn;
35902 if(this.title.length){
35905 cls: 'roo-brick-title',
35910 if(this.html.length){
35913 cls: 'roo-brick-text',
35920 if(this.bgimage.length){
35923 cls: 'roo-brick-image-view',
35931 initEvents: function()
35933 if(this.title.length || this.html.length){
35934 this.el.on('mouseenter' ,this.enter, this);
35935 this.el.on('mouseleave', this.leave, this);
35938 Roo.EventManager.onWindowResize(this.resize, this);
35940 if(this.bgimage.length){
35941 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35942 this.imageEl.on('load', this.onImageLoad, this);
35949 onImageLoad : function()
35954 resize : function()
35956 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35958 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35960 if(this.bgimage.length){
35961 var image = this.el.select('.roo-brick-image-view', true).first();
35963 image.setWidth(paragraph.getWidth());
35966 image.setHeight(paragraph.getWidth());
35969 this.el.setHeight(image.getHeight());
35970 paragraph.setHeight(image.getHeight());
35976 enter: function(e, el)
35978 e.preventDefault();
35980 if(this.bgimage.length){
35981 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35982 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35986 leave: function(e, el)
35988 e.preventDefault();
35990 if(this.bgimage.length){
35991 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35992 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36007 * @class Roo.bootstrap.NumberField
36008 * @extends Roo.bootstrap.Input
36009 * Bootstrap NumberField class
36015 * Create a new NumberField
36016 * @param {Object} config The config object
36019 Roo.bootstrap.NumberField = function(config){
36020 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36023 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36026 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36028 allowDecimals : true,
36030 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36032 decimalSeparator : ".",
36034 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36036 decimalPrecision : 2,
36038 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36040 allowNegative : true,
36043 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36047 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36049 minValue : Number.NEGATIVE_INFINITY,
36051 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36053 maxValue : Number.MAX_VALUE,
36055 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36057 minText : "The minimum value for this field is {0}",
36059 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36061 maxText : "The maximum value for this field is {0}",
36063 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36064 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36066 nanText : "{0} is not a valid number",
36068 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36070 thousandsDelimiter : false,
36072 * @cfg {String} valueAlign alignment of value
36074 valueAlign : "left",
36076 getAutoCreate : function()
36078 var hiddenInput = {
36082 cls: 'hidden-number-input'
36086 hiddenInput.name = this.name;
36091 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36093 this.name = hiddenInput.name;
36095 if(cfg.cn.length > 0) {
36096 cfg.cn.push(hiddenInput);
36103 initEvents : function()
36105 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36107 var allowed = "0123456789";
36109 if(this.allowDecimals){
36110 allowed += this.decimalSeparator;
36113 if(this.allowNegative){
36117 if(this.thousandsDelimiter) {
36121 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36123 var keyPress = function(e){
36125 var k = e.getKey();
36127 var c = e.getCharCode();
36130 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36131 allowed.indexOf(String.fromCharCode(c)) === -1
36137 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36141 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36146 this.el.on("keypress", keyPress, this);
36149 validateValue : function(value)
36152 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36156 var num = this.parseValue(value);
36159 this.markInvalid(String.format(this.nanText, value));
36163 if(num < this.minValue){
36164 this.markInvalid(String.format(this.minText, this.minValue));
36168 if(num > this.maxValue){
36169 this.markInvalid(String.format(this.maxText, this.maxValue));
36176 getValue : function()
36178 var v = this.hiddenEl().getValue();
36180 return this.fixPrecision(this.parseValue(v));
36183 parseValue : function(value)
36185 if(this.thousandsDelimiter) {
36187 r = new RegExp(",", "g");
36188 value = value.replace(r, "");
36191 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36192 return isNaN(value) ? '' : value;
36195 fixPrecision : function(value)
36197 if(this.thousandsDelimiter) {
36199 r = new RegExp(",", "g");
36200 value = value.replace(r, "");
36203 var nan = isNaN(value);
36205 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36206 return nan ? '' : value;
36208 return parseFloat(value).toFixed(this.decimalPrecision);
36211 setValue : function(v)
36213 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36219 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36221 this.inputEl().dom.value = (v == '') ? '' :
36222 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36224 if(!this.allowZero && v === '0') {
36225 this.hiddenEl().dom.value = '';
36226 this.inputEl().dom.value = '';
36233 decimalPrecisionFcn : function(v)
36235 return Math.floor(v);
36238 beforeBlur : function()
36240 var v = this.parseValue(this.getRawValue());
36242 if(v || v === 0 || v === ''){
36247 hiddenEl : function()
36249 return this.el.select('input.hidden-number-input',true).first();
36261 * @class Roo.bootstrap.DocumentSlider
36262 * @extends Roo.bootstrap.Component
36263 * Bootstrap DocumentSlider class
36266 * Create a new DocumentViewer
36267 * @param {Object} config The config object
36270 Roo.bootstrap.DocumentSlider = function(config){
36271 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36278 * Fire after initEvent
36279 * @param {Roo.bootstrap.DocumentSlider} this
36284 * Fire after update
36285 * @param {Roo.bootstrap.DocumentSlider} this
36291 * @param {Roo.bootstrap.DocumentSlider} this
36297 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36303 getAutoCreate : function()
36307 cls : 'roo-document-slider',
36311 cls : 'roo-document-slider-header',
36315 cls : 'roo-document-slider-header-title'
36321 cls : 'roo-document-slider-body',
36325 cls : 'roo-document-slider-prev',
36329 cls : 'fa fa-chevron-left'
36335 cls : 'roo-document-slider-thumb',
36339 cls : 'roo-document-slider-image'
36345 cls : 'roo-document-slider-next',
36349 cls : 'fa fa-chevron-right'
36361 initEvents : function()
36363 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36364 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36366 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36367 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36369 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36370 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36372 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36373 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36375 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36376 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36378 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36379 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36381 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36382 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36384 this.thumbEl.on('click', this.onClick, this);
36386 this.prevIndicator.on('click', this.prev, this);
36388 this.nextIndicator.on('click', this.next, this);
36392 initial : function()
36394 if(this.files.length){
36395 this.indicator = 1;
36399 this.fireEvent('initial', this);
36402 update : function()
36404 this.imageEl.attr('src', this.files[this.indicator - 1]);
36406 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36408 this.prevIndicator.show();
36410 if(this.indicator == 1){
36411 this.prevIndicator.hide();
36414 this.nextIndicator.show();
36416 if(this.indicator == this.files.length){
36417 this.nextIndicator.hide();
36420 this.thumbEl.scrollTo('top');
36422 this.fireEvent('update', this);
36425 onClick : function(e)
36427 e.preventDefault();
36429 this.fireEvent('click', this);
36434 e.preventDefault();
36436 this.indicator = Math.max(1, this.indicator - 1);
36443 e.preventDefault();
36445 this.indicator = Math.min(this.files.length, this.indicator + 1);
36459 * @class Roo.bootstrap.RadioSet
36460 * @extends Roo.bootstrap.Input
36461 * Bootstrap RadioSet class
36462 * @cfg {String} indicatorpos (left|right) default left
36463 * @cfg {Boolean} inline (true|false) inline the element (default true)
36464 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36466 * Create a new RadioSet
36467 * @param {Object} config The config object
36470 Roo.bootstrap.RadioSet = function(config){
36472 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36476 Roo.bootstrap.RadioSet.register(this);
36481 * Fires when the element is checked or unchecked.
36482 * @param {Roo.bootstrap.RadioSet} this This radio
36483 * @param {Roo.bootstrap.Radio} item The checked item
36488 * Fires when the element is click.
36489 * @param {Roo.bootstrap.RadioSet} this This radio set
36490 * @param {Roo.bootstrap.Radio} item The checked item
36491 * @param {Roo.EventObject} e The event object
36498 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36506 indicatorpos : 'left',
36508 getAutoCreate : function()
36512 cls : 'roo-radio-set-label',
36516 html : this.fieldLabel
36520 if (Roo.bootstrap.version == 3) {
36523 if(this.indicatorpos == 'left'){
36526 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36527 tooltip : 'This field is required'
36532 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36533 tooltip : 'This field is required'
36539 cls : 'roo-radio-set-items'
36542 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36544 if (align === 'left' && this.fieldLabel.length) {
36547 cls : "roo-radio-set-right",
36553 if(this.labelWidth > 12){
36554 label.style = "width: " + this.labelWidth + 'px';
36557 if(this.labelWidth < 13 && this.labelmd == 0){
36558 this.labelmd = this.labelWidth;
36561 if(this.labellg > 0){
36562 label.cls += ' col-lg-' + this.labellg;
36563 items.cls += ' col-lg-' + (12 - this.labellg);
36566 if(this.labelmd > 0){
36567 label.cls += ' col-md-' + this.labelmd;
36568 items.cls += ' col-md-' + (12 - this.labelmd);
36571 if(this.labelsm > 0){
36572 label.cls += ' col-sm-' + this.labelsm;
36573 items.cls += ' col-sm-' + (12 - this.labelsm);
36576 if(this.labelxs > 0){
36577 label.cls += ' col-xs-' + this.labelxs;
36578 items.cls += ' col-xs-' + (12 - this.labelxs);
36584 cls : 'roo-radio-set',
36588 cls : 'roo-radio-set-input',
36591 value : this.value ? this.value : ''
36598 if(this.weight.length){
36599 cfg.cls += ' roo-radio-' + this.weight;
36603 cfg.cls += ' roo-radio-set-inline';
36607 ['xs','sm','md','lg'].map(function(size){
36608 if (settings[size]) {
36609 cfg.cls += ' col-' + size + '-' + settings[size];
36617 initEvents : function()
36619 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36620 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36622 if(!this.fieldLabel.length){
36623 this.labelEl.hide();
36626 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36627 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36629 this.indicator = this.indicatorEl();
36631 if(this.indicator){
36632 this.indicator.addClass('invisible');
36635 this.originalValue = this.getValue();
36639 inputEl: function ()
36641 return this.el.select('.roo-radio-set-input', true).first();
36644 getChildContainer : function()
36646 return this.itemsEl;
36649 register : function(item)
36651 this.radioes.push(item);
36655 validate : function()
36657 if(this.getVisibilityEl().hasClass('hidden')){
36663 Roo.each(this.radioes, function(i){
36672 if(this.allowBlank) {
36676 if(this.disabled || valid){
36681 this.markInvalid();
36686 markValid : function()
36688 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36689 this.indicatorEl().removeClass('visible');
36690 this.indicatorEl().addClass('invisible');
36694 if (Roo.bootstrap.version == 3) {
36695 this.el.removeClass([this.invalidClass, this.validClass]);
36696 this.el.addClass(this.validClass);
36698 this.el.removeClass(['is-invalid','is-valid']);
36699 this.el.addClass(['is-valid']);
36701 this.fireEvent('valid', this);
36704 markInvalid : function(msg)
36706 if(this.allowBlank || this.disabled){
36710 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36711 this.indicatorEl().removeClass('invisible');
36712 this.indicatorEl().addClass('visible');
36714 if (Roo.bootstrap.version == 3) {
36715 this.el.removeClass([this.invalidClass, this.validClass]);
36716 this.el.addClass(this.invalidClass);
36718 this.el.removeClass(['is-invalid','is-valid']);
36719 this.el.addClass(['is-invalid']);
36722 this.fireEvent('invalid', this, msg);
36726 setValue : function(v, suppressEvent)
36728 if(this.value === v){
36735 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36738 Roo.each(this.radioes, function(i){
36740 i.el.removeClass('checked');
36743 Roo.each(this.radioes, function(i){
36745 if(i.value === v || i.value.toString() === v.toString()){
36747 i.el.addClass('checked');
36749 if(suppressEvent !== true){
36750 this.fireEvent('check', this, i);
36761 clearInvalid : function(){
36763 if(!this.el || this.preventMark){
36767 this.el.removeClass([this.invalidClass]);
36769 this.fireEvent('valid', this);
36774 Roo.apply(Roo.bootstrap.RadioSet, {
36778 register : function(set)
36780 this.groups[set.name] = set;
36783 get: function(name)
36785 if (typeof(this.groups[name]) == 'undefined') {
36789 return this.groups[name] ;
36795 * Ext JS Library 1.1.1
36796 * Copyright(c) 2006-2007, Ext JS, LLC.
36798 * Originally Released Under LGPL - original licence link has changed is not relivant.
36801 * <script type="text/javascript">
36806 * @class Roo.bootstrap.SplitBar
36807 * @extends Roo.util.Observable
36808 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36812 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36813 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36814 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36815 split.minSize = 100;
36816 split.maxSize = 600;
36817 split.animate = true;
36818 split.on('moved', splitterMoved);
36821 * Create a new SplitBar
36822 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36823 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36824 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36825 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36826 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36827 position of the SplitBar).
36829 Roo.bootstrap.SplitBar = function(cfg){
36834 // dragElement : elm
36835 // resizingElement: el,
36837 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36838 // placement : Roo.bootstrap.SplitBar.LEFT ,
36839 // existingProxy ???
36842 this.el = Roo.get(cfg.dragElement, true);
36843 this.el.dom.unselectable = "on";
36845 this.resizingEl = Roo.get(cfg.resizingElement, true);
36849 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36850 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36853 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36856 * The minimum size of the resizing element. (Defaults to 0)
36862 * The maximum size of the resizing element. (Defaults to 2000)
36865 this.maxSize = 2000;
36868 * Whether to animate the transition to the new size
36871 this.animate = false;
36874 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36877 this.useShim = false;
36882 if(!cfg.existingProxy){
36884 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36886 this.proxy = Roo.get(cfg.existingProxy).dom;
36889 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36892 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36895 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36898 this.dragSpecs = {};
36901 * @private The adapter to use to positon and resize elements
36903 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36904 this.adapter.init(this);
36906 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36908 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36909 this.el.addClass("roo-splitbar-h");
36912 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36913 this.el.addClass("roo-splitbar-v");
36919 * Fires when the splitter is moved (alias for {@link #event-moved})
36920 * @param {Roo.bootstrap.SplitBar} this
36921 * @param {Number} newSize the new width or height
36926 * Fires when the splitter is moved
36927 * @param {Roo.bootstrap.SplitBar} this
36928 * @param {Number} newSize the new width or height
36932 * @event beforeresize
36933 * Fires before the splitter is dragged
36934 * @param {Roo.bootstrap.SplitBar} this
36936 "beforeresize" : true,
36938 "beforeapply" : true
36941 Roo.util.Observable.call(this);
36944 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36945 onStartProxyDrag : function(x, y){
36946 this.fireEvent("beforeresize", this);
36948 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36950 o.enableDisplayMode("block");
36951 // all splitbars share the same overlay
36952 Roo.bootstrap.SplitBar.prototype.overlay = o;
36954 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36955 this.overlay.show();
36956 Roo.get(this.proxy).setDisplayed("block");
36957 var size = this.adapter.getElementSize(this);
36958 this.activeMinSize = this.getMinimumSize();;
36959 this.activeMaxSize = this.getMaximumSize();;
36960 var c1 = size - this.activeMinSize;
36961 var c2 = Math.max(this.activeMaxSize - size, 0);
36962 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36963 this.dd.resetConstraints();
36964 this.dd.setXConstraint(
36965 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36966 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36968 this.dd.setYConstraint(0, 0);
36970 this.dd.resetConstraints();
36971 this.dd.setXConstraint(0, 0);
36972 this.dd.setYConstraint(
36973 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36974 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36977 this.dragSpecs.startSize = size;
36978 this.dragSpecs.startPoint = [x, y];
36979 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36983 * @private Called after the drag operation by the DDProxy
36985 onEndProxyDrag : function(e){
36986 Roo.get(this.proxy).setDisplayed(false);
36987 var endPoint = Roo.lib.Event.getXY(e);
36989 this.overlay.hide();
36992 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36993 newSize = this.dragSpecs.startSize +
36994 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36995 endPoint[0] - this.dragSpecs.startPoint[0] :
36996 this.dragSpecs.startPoint[0] - endPoint[0]
36999 newSize = this.dragSpecs.startSize +
37000 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37001 endPoint[1] - this.dragSpecs.startPoint[1] :
37002 this.dragSpecs.startPoint[1] - endPoint[1]
37005 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37006 if(newSize != this.dragSpecs.startSize){
37007 if(this.fireEvent('beforeapply', this, newSize) !== false){
37008 this.adapter.setElementSize(this, newSize);
37009 this.fireEvent("moved", this, newSize);
37010 this.fireEvent("resize", this, newSize);
37016 * Get the adapter this SplitBar uses
37017 * @return The adapter object
37019 getAdapter : function(){
37020 return this.adapter;
37024 * Set the adapter this SplitBar uses
37025 * @param {Object} adapter A SplitBar adapter object
37027 setAdapter : function(adapter){
37028 this.adapter = adapter;
37029 this.adapter.init(this);
37033 * Gets the minimum size for the resizing element
37034 * @return {Number} The minimum size
37036 getMinimumSize : function(){
37037 return this.minSize;
37041 * Sets the minimum size for the resizing element
37042 * @param {Number} minSize The minimum size
37044 setMinimumSize : function(minSize){
37045 this.minSize = minSize;
37049 * Gets the maximum size for the resizing element
37050 * @return {Number} The maximum size
37052 getMaximumSize : function(){
37053 return this.maxSize;
37057 * Sets the maximum size for the resizing element
37058 * @param {Number} maxSize The maximum size
37060 setMaximumSize : function(maxSize){
37061 this.maxSize = maxSize;
37065 * Sets the initialize size for the resizing element
37066 * @param {Number} size The initial size
37068 setCurrentSize : function(size){
37069 var oldAnimate = this.animate;
37070 this.animate = false;
37071 this.adapter.setElementSize(this, size);
37072 this.animate = oldAnimate;
37076 * Destroy this splitbar.
37077 * @param {Boolean} removeEl True to remove the element
37079 destroy : function(removeEl){
37081 this.shim.remove();
37084 this.proxy.parentNode.removeChild(this.proxy);
37092 * @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.
37094 Roo.bootstrap.SplitBar.createProxy = function(dir){
37095 var proxy = new Roo.Element(document.createElement("div"));
37096 proxy.unselectable();
37097 var cls = 'roo-splitbar-proxy';
37098 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37099 document.body.appendChild(proxy.dom);
37104 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37105 * Default Adapter. It assumes the splitter and resizing element are not positioned
37106 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37108 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37111 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37112 // do nothing for now
37113 init : function(s){
37117 * Called before drag operations to get the current size of the resizing element.
37118 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37120 getElementSize : function(s){
37121 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37122 return s.resizingEl.getWidth();
37124 return s.resizingEl.getHeight();
37129 * Called after drag operations to set the size of the resizing element.
37130 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37131 * @param {Number} newSize The new size to set
37132 * @param {Function} onComplete A function to be invoked when resizing is complete
37134 setElementSize : function(s, newSize, onComplete){
37135 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37137 s.resizingEl.setWidth(newSize);
37139 onComplete(s, newSize);
37142 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37147 s.resizingEl.setHeight(newSize);
37149 onComplete(s, newSize);
37152 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37159 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37160 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37161 * Adapter that moves the splitter element to align with the resized sizing element.
37162 * Used with an absolute positioned SplitBar.
37163 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37164 * document.body, make sure you assign an id to the body element.
37166 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37167 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37168 this.container = Roo.get(container);
37171 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37172 init : function(s){
37173 this.basic.init(s);
37176 getElementSize : function(s){
37177 return this.basic.getElementSize(s);
37180 setElementSize : function(s, newSize, onComplete){
37181 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37184 moveSplitter : function(s){
37185 var yes = Roo.bootstrap.SplitBar;
37186 switch(s.placement){
37188 s.el.setX(s.resizingEl.getRight());
37191 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37194 s.el.setY(s.resizingEl.getBottom());
37197 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37204 * Orientation constant - Create a vertical SplitBar
37208 Roo.bootstrap.SplitBar.VERTICAL = 1;
37211 * Orientation constant - Create a horizontal SplitBar
37215 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37218 * Placement constant - The resizing element is to the left of the splitter element
37222 Roo.bootstrap.SplitBar.LEFT = 1;
37225 * Placement constant - The resizing element is to the right of the splitter element
37229 Roo.bootstrap.SplitBar.RIGHT = 2;
37232 * Placement constant - The resizing element is positioned above the splitter element
37236 Roo.bootstrap.SplitBar.TOP = 3;
37239 * Placement constant - The resizing element is positioned under splitter element
37243 Roo.bootstrap.SplitBar.BOTTOM = 4;
37244 Roo.namespace("Roo.bootstrap.layout");/*
37246 * Ext JS Library 1.1.1
37247 * Copyright(c) 2006-2007, Ext JS, LLC.
37249 * Originally Released Under LGPL - original licence link has changed is not relivant.
37252 * <script type="text/javascript">
37256 * @class Roo.bootstrap.layout.Manager
37257 * @extends Roo.bootstrap.Component
37258 * Base class for layout managers.
37260 Roo.bootstrap.layout.Manager = function(config)
37262 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37268 /** false to disable window resize monitoring @type Boolean */
37269 this.monitorWindowResize = true;
37274 * Fires when a layout is performed.
37275 * @param {Roo.LayoutManager} this
37279 * @event regionresized
37280 * Fires when the user resizes a region.
37281 * @param {Roo.LayoutRegion} region The resized region
37282 * @param {Number} newSize The new size (width for east/west, height for north/south)
37284 "regionresized" : true,
37286 * @event regioncollapsed
37287 * Fires when a region is collapsed.
37288 * @param {Roo.LayoutRegion} region The collapsed region
37290 "regioncollapsed" : true,
37292 * @event regionexpanded
37293 * Fires when a region is expanded.
37294 * @param {Roo.LayoutRegion} region The expanded region
37296 "regionexpanded" : true
37298 this.updating = false;
37301 this.el = Roo.get(config.el);
37307 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37312 monitorWindowResize : true,
37318 onRender : function(ct, position)
37321 this.el = Roo.get(ct);
37324 //this.fireEvent('render',this);
37328 initEvents: function()
37332 // ie scrollbar fix
37333 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37334 document.body.scroll = "no";
37335 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37336 this.el.position('relative');
37338 this.id = this.el.id;
37339 this.el.addClass("roo-layout-container");
37340 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37341 if(this.el.dom != document.body ) {
37342 this.el.on('resize', this.layout,this);
37343 this.el.on('show', this.layout,this);
37349 * Returns true if this layout is currently being updated
37350 * @return {Boolean}
37352 isUpdating : function(){
37353 return this.updating;
37357 * Suspend the LayoutManager from doing auto-layouts while
37358 * making multiple add or remove calls
37360 beginUpdate : function(){
37361 this.updating = true;
37365 * Restore auto-layouts and optionally disable the manager from performing a layout
37366 * @param {Boolean} noLayout true to disable a layout update
37368 endUpdate : function(noLayout){
37369 this.updating = false;
37375 layout: function(){
37379 onRegionResized : function(region, newSize){
37380 this.fireEvent("regionresized", region, newSize);
37384 onRegionCollapsed : function(region){
37385 this.fireEvent("regioncollapsed", region);
37388 onRegionExpanded : function(region){
37389 this.fireEvent("regionexpanded", region);
37393 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37394 * performs box-model adjustments.
37395 * @return {Object} The size as an object {width: (the width), height: (the height)}
37397 getViewSize : function()
37400 if(this.el.dom != document.body){
37401 size = this.el.getSize();
37403 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37405 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37406 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37411 * Returns the Element this layout is bound to.
37412 * @return {Roo.Element}
37414 getEl : function(){
37419 * Returns the specified region.
37420 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37421 * @return {Roo.LayoutRegion}
37423 getRegion : function(target){
37424 return this.regions[target.toLowerCase()];
37427 onWindowResize : function(){
37428 if(this.monitorWindowResize){
37435 * Ext JS Library 1.1.1
37436 * Copyright(c) 2006-2007, Ext JS, LLC.
37438 * Originally Released Under LGPL - original licence link has changed is not relivant.
37441 * <script type="text/javascript">
37444 * @class Roo.bootstrap.layout.Border
37445 * @extends Roo.bootstrap.layout.Manager
37446 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37447 * please see: examples/bootstrap/nested.html<br><br>
37449 <b>The container the layout is rendered into can be either the body element or any other element.
37450 If it is not the body element, the container needs to either be an absolute positioned element,
37451 or you will need to add "position:relative" to the css of the container. You will also need to specify
37452 the container size if it is not the body element.</b>
37455 * Create a new Border
37456 * @param {Object} config Configuration options
37458 Roo.bootstrap.layout.Border = function(config){
37459 config = config || {};
37460 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37464 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37465 if(config[region]){
37466 config[region].region = region;
37467 this.addRegion(config[region]);
37473 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37475 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37477 parent : false, // this might point to a 'nest' or a ???
37480 * Creates and adds a new region if it doesn't already exist.
37481 * @param {String} target The target region key (north, south, east, west or center).
37482 * @param {Object} config The regions config object
37483 * @return {BorderLayoutRegion} The new region
37485 addRegion : function(config)
37487 if(!this.regions[config.region]){
37488 var r = this.factory(config);
37489 this.bindRegion(r);
37491 return this.regions[config.region];
37495 bindRegion : function(r){
37496 this.regions[r.config.region] = r;
37498 r.on("visibilitychange", this.layout, this);
37499 r.on("paneladded", this.layout, this);
37500 r.on("panelremoved", this.layout, this);
37501 r.on("invalidated", this.layout, this);
37502 r.on("resized", this.onRegionResized, this);
37503 r.on("collapsed", this.onRegionCollapsed, this);
37504 r.on("expanded", this.onRegionExpanded, this);
37508 * Performs a layout update.
37510 layout : function()
37512 if(this.updating) {
37516 // render all the rebions if they have not been done alreayd?
37517 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37518 if(this.regions[region] && !this.regions[region].bodyEl){
37519 this.regions[region].onRender(this.el)
37523 var size = this.getViewSize();
37524 var w = size.width;
37525 var h = size.height;
37530 //var x = 0, y = 0;
37532 var rs = this.regions;
37533 var north = rs["north"];
37534 var south = rs["south"];
37535 var west = rs["west"];
37536 var east = rs["east"];
37537 var center = rs["center"];
37538 //if(this.hideOnLayout){ // not supported anymore
37539 //c.el.setStyle("display", "none");
37541 if(north && north.isVisible()){
37542 var b = north.getBox();
37543 var m = north.getMargins();
37544 b.width = w - (m.left+m.right);
37547 centerY = b.height + b.y + m.bottom;
37548 centerH -= centerY;
37549 north.updateBox(this.safeBox(b));
37551 if(south && south.isVisible()){
37552 var b = south.getBox();
37553 var m = south.getMargins();
37554 b.width = w - (m.left+m.right);
37556 var totalHeight = (b.height + m.top + m.bottom);
37557 b.y = h - totalHeight + m.top;
37558 centerH -= totalHeight;
37559 south.updateBox(this.safeBox(b));
37561 if(west && west.isVisible()){
37562 var b = west.getBox();
37563 var m = west.getMargins();
37564 b.height = centerH - (m.top+m.bottom);
37566 b.y = centerY + m.top;
37567 var totalWidth = (b.width + m.left + m.right);
37568 centerX += totalWidth;
37569 centerW -= totalWidth;
37570 west.updateBox(this.safeBox(b));
37572 if(east && east.isVisible()){
37573 var b = east.getBox();
37574 var m = east.getMargins();
37575 b.height = centerH - (m.top+m.bottom);
37576 var totalWidth = (b.width + m.left + m.right);
37577 b.x = w - totalWidth + m.left;
37578 b.y = centerY + m.top;
37579 centerW -= totalWidth;
37580 east.updateBox(this.safeBox(b));
37583 var m = center.getMargins();
37585 x: centerX + m.left,
37586 y: centerY + m.top,
37587 width: centerW - (m.left+m.right),
37588 height: centerH - (m.top+m.bottom)
37590 //if(this.hideOnLayout){
37591 //center.el.setStyle("display", "block");
37593 center.updateBox(this.safeBox(centerBox));
37596 this.fireEvent("layout", this);
37600 safeBox : function(box){
37601 box.width = Math.max(0, box.width);
37602 box.height = Math.max(0, box.height);
37607 * Adds a ContentPanel (or subclass) to this layout.
37608 * @param {String} target The target region key (north, south, east, west or center).
37609 * @param {Roo.ContentPanel} panel The panel to add
37610 * @return {Roo.ContentPanel} The added panel
37612 add : function(target, panel){
37614 target = target.toLowerCase();
37615 return this.regions[target].add(panel);
37619 * Remove a ContentPanel (or subclass) to this layout.
37620 * @param {String} target The target region key (north, south, east, west or center).
37621 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37622 * @return {Roo.ContentPanel} The removed panel
37624 remove : function(target, panel){
37625 target = target.toLowerCase();
37626 return this.regions[target].remove(panel);
37630 * Searches all regions for a panel with the specified id
37631 * @param {String} panelId
37632 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37634 findPanel : function(panelId){
37635 var rs = this.regions;
37636 for(var target in rs){
37637 if(typeof rs[target] != "function"){
37638 var p = rs[target].getPanel(panelId);
37648 * Searches all regions for a panel with the specified id and activates (shows) it.
37649 * @param {String/ContentPanel} panelId The panels id or the panel itself
37650 * @return {Roo.ContentPanel} The shown panel or null
37652 showPanel : function(panelId) {
37653 var rs = this.regions;
37654 for(var target in rs){
37655 var r = rs[target];
37656 if(typeof r != "function"){
37657 if(r.hasPanel(panelId)){
37658 return r.showPanel(panelId);
37666 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37667 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37670 restoreState : function(provider){
37672 provider = Roo.state.Manager;
37674 var sm = new Roo.LayoutStateManager();
37675 sm.init(this, provider);
37681 * Adds a xtype elements to the layout.
37685 xtype : 'ContentPanel',
37692 xtype : 'NestedLayoutPanel',
37698 items : [ ... list of content panels or nested layout panels.. ]
37702 * @param {Object} cfg Xtype definition of item to add.
37704 addxtype : function(cfg)
37706 // basically accepts a pannel...
37707 // can accept a layout region..!?!?
37708 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37711 // theory? children can only be panels??
37713 //if (!cfg.xtype.match(/Panel$/)) {
37718 if (typeof(cfg.region) == 'undefined') {
37719 Roo.log("Failed to add Panel, region was not set");
37723 var region = cfg.region;
37729 xitems = cfg.items;
37734 if ( region == 'center') {
37735 Roo.log("Center: " + cfg.title);
37741 case 'Content': // ContentPanel (el, cfg)
37742 case 'Scroll': // ContentPanel (el, cfg)
37744 cfg.autoCreate = cfg.autoCreate || true;
37745 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37747 // var el = this.el.createChild();
37748 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37751 this.add(region, ret);
37755 case 'TreePanel': // our new panel!
37756 cfg.el = this.el.createChild();
37757 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37758 this.add(region, ret);
37763 // create a new Layout (which is a Border Layout...
37765 var clayout = cfg.layout;
37766 clayout.el = this.el.createChild();
37767 clayout.items = clayout.items || [];
37771 // replace this exitems with the clayout ones..
37772 xitems = clayout.items;
37774 // force background off if it's in center...
37775 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37776 cfg.background = false;
37778 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37781 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37782 //console.log('adding nested layout panel ' + cfg.toSource());
37783 this.add(region, ret);
37784 nb = {}; /// find first...
37789 // needs grid and region
37791 //var el = this.getRegion(region).el.createChild();
37793 *var el = this.el.createChild();
37794 // create the grid first...
37795 cfg.grid.container = el;
37796 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37799 if (region == 'center' && this.active ) {
37800 cfg.background = false;
37803 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37805 this.add(region, ret);
37807 if (cfg.background) {
37808 // render grid on panel activation (if panel background)
37809 ret.on('activate', function(gp) {
37810 if (!gp.grid.rendered) {
37811 // gp.grid.render(el);
37815 // cfg.grid.render(el);
37821 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37822 // it was the old xcomponent building that caused this before.
37823 // espeically if border is the top element in the tree.
37833 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37835 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37836 this.add(region, ret);
37840 throw "Can not add '" + cfg.xtype + "' to Border";
37846 this.beginUpdate();
37850 Roo.each(xitems, function(i) {
37851 region = nb && i.region ? i.region : false;
37853 var add = ret.addxtype(i);
37856 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37857 if (!i.background) {
37858 abn[region] = nb[region] ;
37865 // make the last non-background panel active..
37866 //if (nb) { Roo.log(abn); }
37869 for(var r in abn) {
37870 region = this.getRegion(r);
37872 // tried using nb[r], but it does not work..
37874 region.showPanel(abn[r]);
37885 factory : function(cfg)
37888 var validRegions = Roo.bootstrap.layout.Border.regions;
37890 var target = cfg.region;
37893 var r = Roo.bootstrap.layout;
37897 return new r.North(cfg);
37899 return new r.South(cfg);
37901 return new r.East(cfg);
37903 return new r.West(cfg);
37905 return new r.Center(cfg);
37907 throw 'Layout region "'+target+'" not supported.';
37914 * Ext JS Library 1.1.1
37915 * Copyright(c) 2006-2007, Ext JS, LLC.
37917 * Originally Released Under LGPL - original licence link has changed is not relivant.
37920 * <script type="text/javascript">
37924 * @class Roo.bootstrap.layout.Basic
37925 * @extends Roo.util.Observable
37926 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37927 * and does not have a titlebar, tabs or any other features. All it does is size and position
37928 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37929 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37930 * @cfg {string} region the region that it inhabits..
37931 * @cfg {bool} skipConfig skip config?
37935 Roo.bootstrap.layout.Basic = function(config){
37937 this.mgr = config.mgr;
37939 this.position = config.region;
37941 var skipConfig = config.skipConfig;
37945 * @scope Roo.BasicLayoutRegion
37949 * @event beforeremove
37950 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37951 * @param {Roo.LayoutRegion} this
37952 * @param {Roo.ContentPanel} panel The panel
37953 * @param {Object} e The cancel event object
37955 "beforeremove" : true,
37957 * @event invalidated
37958 * Fires when the layout for this region is changed.
37959 * @param {Roo.LayoutRegion} this
37961 "invalidated" : true,
37963 * @event visibilitychange
37964 * Fires when this region is shown or hidden
37965 * @param {Roo.LayoutRegion} this
37966 * @param {Boolean} visibility true or false
37968 "visibilitychange" : true,
37970 * @event paneladded
37971 * Fires when a panel is added.
37972 * @param {Roo.LayoutRegion} this
37973 * @param {Roo.ContentPanel} panel The panel
37975 "paneladded" : true,
37977 * @event panelremoved
37978 * Fires when a panel is removed.
37979 * @param {Roo.LayoutRegion} this
37980 * @param {Roo.ContentPanel} panel The panel
37982 "panelremoved" : true,
37984 * @event beforecollapse
37985 * Fires when this region before collapse.
37986 * @param {Roo.LayoutRegion} this
37988 "beforecollapse" : true,
37991 * Fires when this region is collapsed.
37992 * @param {Roo.LayoutRegion} this
37994 "collapsed" : true,
37997 * Fires when this region is expanded.
37998 * @param {Roo.LayoutRegion} this
38003 * Fires when this region is slid into view.
38004 * @param {Roo.LayoutRegion} this
38006 "slideshow" : true,
38009 * Fires when this region slides out of view.
38010 * @param {Roo.LayoutRegion} this
38012 "slidehide" : true,
38014 * @event panelactivated
38015 * Fires when a panel is activated.
38016 * @param {Roo.LayoutRegion} this
38017 * @param {Roo.ContentPanel} panel The activated panel
38019 "panelactivated" : true,
38022 * Fires when the user resizes this region.
38023 * @param {Roo.LayoutRegion} this
38024 * @param {Number} newSize The new size (width for east/west, height for north/south)
38028 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38029 this.panels = new Roo.util.MixedCollection();
38030 this.panels.getKey = this.getPanelId.createDelegate(this);
38032 this.activePanel = null;
38033 // ensure listeners are added...
38035 if (config.listeners || config.events) {
38036 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38037 listeners : config.listeners || {},
38038 events : config.events || {}
38042 if(skipConfig !== true){
38043 this.applyConfig(config);
38047 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38049 getPanelId : function(p){
38053 applyConfig : function(config){
38054 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38055 this.config = config;
38060 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38061 * the width, for horizontal (north, south) the height.
38062 * @param {Number} newSize The new width or height
38064 resizeTo : function(newSize){
38065 var el = this.el ? this.el :
38066 (this.activePanel ? this.activePanel.getEl() : null);
38068 switch(this.position){
38071 el.setWidth(newSize);
38072 this.fireEvent("resized", this, newSize);
38076 el.setHeight(newSize);
38077 this.fireEvent("resized", this, newSize);
38083 getBox : function(){
38084 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38087 getMargins : function(){
38088 return this.margins;
38091 updateBox : function(box){
38093 var el = this.activePanel.getEl();
38094 el.dom.style.left = box.x + "px";
38095 el.dom.style.top = box.y + "px";
38096 this.activePanel.setSize(box.width, box.height);
38100 * Returns the container element for this region.
38101 * @return {Roo.Element}
38103 getEl : function(){
38104 return this.activePanel;
38108 * Returns true if this region is currently visible.
38109 * @return {Boolean}
38111 isVisible : function(){
38112 return this.activePanel ? true : false;
38115 setActivePanel : function(panel){
38116 panel = this.getPanel(panel);
38117 if(this.activePanel && this.activePanel != panel){
38118 this.activePanel.setActiveState(false);
38119 this.activePanel.getEl().setLeftTop(-10000,-10000);
38121 this.activePanel = panel;
38122 panel.setActiveState(true);
38124 panel.setSize(this.box.width, this.box.height);
38126 this.fireEvent("panelactivated", this, panel);
38127 this.fireEvent("invalidated");
38131 * Show the specified panel.
38132 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38133 * @return {Roo.ContentPanel} The shown panel or null
38135 showPanel : function(panel){
38136 panel = this.getPanel(panel);
38138 this.setActivePanel(panel);
38144 * Get the active panel for this region.
38145 * @return {Roo.ContentPanel} The active panel or null
38147 getActivePanel : function(){
38148 return this.activePanel;
38152 * Add the passed ContentPanel(s)
38153 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38154 * @return {Roo.ContentPanel} The panel added (if only one was added)
38156 add : function(panel){
38157 if(arguments.length > 1){
38158 for(var i = 0, len = arguments.length; i < len; i++) {
38159 this.add(arguments[i]);
38163 if(this.hasPanel(panel)){
38164 this.showPanel(panel);
38167 var el = panel.getEl();
38168 if(el.dom.parentNode != this.mgr.el.dom){
38169 this.mgr.el.dom.appendChild(el.dom);
38171 if(panel.setRegion){
38172 panel.setRegion(this);
38174 this.panels.add(panel);
38175 el.setStyle("position", "absolute");
38176 if(!panel.background){
38177 this.setActivePanel(panel);
38178 if(this.config.initialSize && this.panels.getCount()==1){
38179 this.resizeTo(this.config.initialSize);
38182 this.fireEvent("paneladded", this, panel);
38187 * Returns true if the panel is in this region.
38188 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38189 * @return {Boolean}
38191 hasPanel : function(panel){
38192 if(typeof panel == "object"){ // must be panel obj
38193 panel = panel.getId();
38195 return this.getPanel(panel) ? true : false;
38199 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38200 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38201 * @param {Boolean} preservePanel Overrides the config preservePanel option
38202 * @return {Roo.ContentPanel} The panel that was removed
38204 remove : function(panel, preservePanel){
38205 panel = this.getPanel(panel);
38210 this.fireEvent("beforeremove", this, panel, e);
38211 if(e.cancel === true){
38214 var panelId = panel.getId();
38215 this.panels.removeKey(panelId);
38220 * Returns the panel specified or null if it's not in this region.
38221 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38222 * @return {Roo.ContentPanel}
38224 getPanel : function(id){
38225 if(typeof id == "object"){ // must be panel obj
38228 return this.panels.get(id);
38232 * Returns this regions position (north/south/east/west/center).
38235 getPosition: function(){
38236 return this.position;
38240 * Ext JS Library 1.1.1
38241 * Copyright(c) 2006-2007, Ext JS, LLC.
38243 * Originally Released Under LGPL - original licence link has changed is not relivant.
38246 * <script type="text/javascript">
38250 * @class Roo.bootstrap.layout.Region
38251 * @extends Roo.bootstrap.layout.Basic
38252 * This class represents a region in a layout manager.
38254 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38255 * @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})
38256 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38257 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38258 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38259 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38260 * @cfg {String} title The title for the region (overrides panel titles)
38261 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38262 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38263 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38264 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38265 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38266 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38267 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38268 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38269 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38270 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38272 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38273 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38274 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38275 * @cfg {Number} width For East/West panels
38276 * @cfg {Number} height For North/South panels
38277 * @cfg {Boolean} split To show the splitter
38278 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38280 * @cfg {string} cls Extra CSS classes to add to region
38282 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38283 * @cfg {string} region the region that it inhabits..
38286 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38287 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38289 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38290 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38291 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38293 Roo.bootstrap.layout.Region = function(config)
38295 this.applyConfig(config);
38297 var mgr = config.mgr;
38298 var pos = config.region;
38299 config.skipConfig = true;
38300 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38303 this.onRender(mgr.el);
38306 this.visible = true;
38307 this.collapsed = false;
38308 this.unrendered_panels = [];
38311 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38313 position: '', // set by wrapper (eg. north/south etc..)
38314 unrendered_panels : null, // unrendered panels.
38316 tabPosition : false,
38318 mgr: false, // points to 'Border'
38321 createBody : function(){
38322 /** This region's body element
38323 * @type Roo.Element */
38324 this.bodyEl = this.el.createChild({
38326 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38330 onRender: function(ctr, pos)
38332 var dh = Roo.DomHelper;
38333 /** This region's container element
38334 * @type Roo.Element */
38335 this.el = dh.append(ctr.dom, {
38337 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38339 /** This region's title element
38340 * @type Roo.Element */
38342 this.titleEl = dh.append(this.el.dom, {
38344 unselectable: "on",
38345 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38347 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38348 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38352 this.titleEl.enableDisplayMode();
38353 /** This region's title text element
38354 * @type HTMLElement */
38355 this.titleTextEl = this.titleEl.dom.firstChild;
38356 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38358 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38359 this.closeBtn.enableDisplayMode();
38360 this.closeBtn.on("click", this.closeClicked, this);
38361 this.closeBtn.hide();
38363 this.createBody(this.config);
38364 if(this.config.hideWhenEmpty){
38366 this.on("paneladded", this.validateVisibility, this);
38367 this.on("panelremoved", this.validateVisibility, this);
38369 if(this.autoScroll){
38370 this.bodyEl.setStyle("overflow", "auto");
38372 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38374 //if(c.titlebar !== false){
38375 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38376 this.titleEl.hide();
38378 this.titleEl.show();
38379 if(this.config.title){
38380 this.titleTextEl.innerHTML = this.config.title;
38384 if(this.config.collapsed){
38385 this.collapse(true);
38387 if(this.config.hidden){
38391 if (this.unrendered_panels && this.unrendered_panels.length) {
38392 for (var i =0;i< this.unrendered_panels.length; i++) {
38393 this.add(this.unrendered_panels[i]);
38395 this.unrendered_panels = null;
38401 applyConfig : function(c)
38404 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38405 var dh = Roo.DomHelper;
38406 if(c.titlebar !== false){
38407 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38408 this.collapseBtn.on("click", this.collapse, this);
38409 this.collapseBtn.enableDisplayMode();
38411 if(c.showPin === true || this.showPin){
38412 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38413 this.stickBtn.enableDisplayMode();
38414 this.stickBtn.on("click", this.expand, this);
38415 this.stickBtn.hide();
38420 /** This region's collapsed element
38421 * @type Roo.Element */
38424 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38425 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38428 if(c.floatable !== false){
38429 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38430 this.collapsedEl.on("click", this.collapseClick, this);
38433 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38434 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38435 id: "message", unselectable: "on", style:{"float":"left"}});
38436 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38438 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38439 this.expandBtn.on("click", this.expand, this);
38443 if(this.collapseBtn){
38444 this.collapseBtn.setVisible(c.collapsible == true);
38447 this.cmargins = c.cmargins || this.cmargins ||
38448 (this.position == "west" || this.position == "east" ?
38449 {top: 0, left: 2, right:2, bottom: 0} :
38450 {top: 2, left: 0, right:0, bottom: 2});
38452 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38455 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38457 this.autoScroll = c.autoScroll || false;
38462 this.duration = c.duration || .30;
38463 this.slideDuration = c.slideDuration || .45;
38468 * Returns true if this region is currently visible.
38469 * @return {Boolean}
38471 isVisible : function(){
38472 return this.visible;
38476 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38477 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38479 //setCollapsedTitle : function(title){
38480 // title = title || " ";
38481 // if(this.collapsedTitleTextEl){
38482 // this.collapsedTitleTextEl.innerHTML = title;
38486 getBox : function(){
38488 // if(!this.collapsed){
38489 b = this.el.getBox(false, true);
38491 // b = this.collapsedEl.getBox(false, true);
38496 getMargins : function(){
38497 return this.margins;
38498 //return this.collapsed ? this.cmargins : this.margins;
38501 highlight : function(){
38502 this.el.addClass("x-layout-panel-dragover");
38505 unhighlight : function(){
38506 this.el.removeClass("x-layout-panel-dragover");
38509 updateBox : function(box)
38511 if (!this.bodyEl) {
38512 return; // not rendered yet..
38516 if(!this.collapsed){
38517 this.el.dom.style.left = box.x + "px";
38518 this.el.dom.style.top = box.y + "px";
38519 this.updateBody(box.width, box.height);
38521 this.collapsedEl.dom.style.left = box.x + "px";
38522 this.collapsedEl.dom.style.top = box.y + "px";
38523 this.collapsedEl.setSize(box.width, box.height);
38526 this.tabs.autoSizeTabs();
38530 updateBody : function(w, h)
38533 this.el.setWidth(w);
38534 w -= this.el.getBorderWidth("rl");
38535 if(this.config.adjustments){
38536 w += this.config.adjustments[0];
38539 if(h !== null && h > 0){
38540 this.el.setHeight(h);
38541 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38542 h -= this.el.getBorderWidth("tb");
38543 if(this.config.adjustments){
38544 h += this.config.adjustments[1];
38546 this.bodyEl.setHeight(h);
38548 h = this.tabs.syncHeight(h);
38551 if(this.panelSize){
38552 w = w !== null ? w : this.panelSize.width;
38553 h = h !== null ? h : this.panelSize.height;
38555 if(this.activePanel){
38556 var el = this.activePanel.getEl();
38557 w = w !== null ? w : el.getWidth();
38558 h = h !== null ? h : el.getHeight();
38559 this.panelSize = {width: w, height: h};
38560 this.activePanel.setSize(w, h);
38562 if(Roo.isIE && this.tabs){
38563 this.tabs.el.repaint();
38568 * Returns the container element for this region.
38569 * @return {Roo.Element}
38571 getEl : function(){
38576 * Hides this region.
38579 //if(!this.collapsed){
38580 this.el.dom.style.left = "-2000px";
38583 // this.collapsedEl.dom.style.left = "-2000px";
38584 // this.collapsedEl.hide();
38586 this.visible = false;
38587 this.fireEvent("visibilitychange", this, false);
38591 * Shows this region if it was previously hidden.
38594 //if(!this.collapsed){
38597 // this.collapsedEl.show();
38599 this.visible = true;
38600 this.fireEvent("visibilitychange", this, true);
38603 closeClicked : function(){
38604 if(this.activePanel){
38605 this.remove(this.activePanel);
38609 collapseClick : function(e){
38611 e.stopPropagation();
38614 e.stopPropagation();
38620 * Collapses this region.
38621 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38624 collapse : function(skipAnim, skipCheck = false){
38625 if(this.collapsed) {
38629 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38631 this.collapsed = true;
38633 this.split.el.hide();
38635 if(this.config.animate && skipAnim !== true){
38636 this.fireEvent("invalidated", this);
38637 this.animateCollapse();
38639 this.el.setLocation(-20000,-20000);
38641 this.collapsedEl.show();
38642 this.fireEvent("collapsed", this);
38643 this.fireEvent("invalidated", this);
38649 animateCollapse : function(){
38654 * Expands this region if it was previously collapsed.
38655 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38656 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38659 expand : function(e, skipAnim){
38661 e.stopPropagation();
38663 if(!this.collapsed || this.el.hasActiveFx()) {
38667 this.afterSlideIn();
38670 this.collapsed = false;
38671 if(this.config.animate && skipAnim !== true){
38672 this.animateExpand();
38676 this.split.el.show();
38678 this.collapsedEl.setLocation(-2000,-2000);
38679 this.collapsedEl.hide();
38680 this.fireEvent("invalidated", this);
38681 this.fireEvent("expanded", this);
38685 animateExpand : function(){
38689 initTabs : function()
38691 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38693 var ts = new Roo.bootstrap.panel.Tabs({
38694 el: this.bodyEl.dom,
38696 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38697 disableTooltips: this.config.disableTabTips,
38698 toolbar : this.config.toolbar
38701 if(this.config.hideTabs){
38702 ts.stripWrap.setDisplayed(false);
38705 ts.resizeTabs = this.config.resizeTabs === true;
38706 ts.minTabWidth = this.config.minTabWidth || 40;
38707 ts.maxTabWidth = this.config.maxTabWidth || 250;
38708 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38709 ts.monitorResize = false;
38710 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38711 ts.bodyEl.addClass('roo-layout-tabs-body');
38712 this.panels.each(this.initPanelAsTab, this);
38715 initPanelAsTab : function(panel){
38716 var ti = this.tabs.addTab(
38720 this.config.closeOnTab && panel.isClosable(),
38723 if(panel.tabTip !== undefined){
38724 ti.setTooltip(panel.tabTip);
38726 ti.on("activate", function(){
38727 this.setActivePanel(panel);
38730 if(this.config.closeOnTab){
38731 ti.on("beforeclose", function(t, e){
38733 this.remove(panel);
38737 panel.tabItem = ti;
38742 updatePanelTitle : function(panel, title)
38744 if(this.activePanel == panel){
38745 this.updateTitle(title);
38748 var ti = this.tabs.getTab(panel.getEl().id);
38750 if(panel.tabTip !== undefined){
38751 ti.setTooltip(panel.tabTip);
38756 updateTitle : function(title){
38757 if(this.titleTextEl && !this.config.title){
38758 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38762 setActivePanel : function(panel)
38764 panel = this.getPanel(panel);
38765 if(this.activePanel && this.activePanel != panel){
38766 if(this.activePanel.setActiveState(false) === false){
38770 this.activePanel = panel;
38771 panel.setActiveState(true);
38772 if(this.panelSize){
38773 panel.setSize(this.panelSize.width, this.panelSize.height);
38776 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38778 this.updateTitle(panel.getTitle());
38780 this.fireEvent("invalidated", this);
38782 this.fireEvent("panelactivated", this, panel);
38786 * Shows the specified panel.
38787 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38788 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38790 showPanel : function(panel)
38792 panel = this.getPanel(panel);
38795 var tab = this.tabs.getTab(panel.getEl().id);
38796 if(tab.isHidden()){
38797 this.tabs.unhideTab(tab.id);
38801 this.setActivePanel(panel);
38808 * Get the active panel for this region.
38809 * @return {Roo.ContentPanel} The active panel or null
38811 getActivePanel : function(){
38812 return this.activePanel;
38815 validateVisibility : function(){
38816 if(this.panels.getCount() < 1){
38817 this.updateTitle(" ");
38818 this.closeBtn.hide();
38821 if(!this.isVisible()){
38828 * Adds the passed ContentPanel(s) to this region.
38829 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38830 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38832 add : function(panel)
38834 if(arguments.length > 1){
38835 for(var i = 0, len = arguments.length; i < len; i++) {
38836 this.add(arguments[i]);
38841 // if we have not been rendered yet, then we can not really do much of this..
38842 if (!this.bodyEl) {
38843 this.unrendered_panels.push(panel);
38850 if(this.hasPanel(panel)){
38851 this.showPanel(panel);
38854 panel.setRegion(this);
38855 this.panels.add(panel);
38856 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38857 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38858 // and hide them... ???
38859 this.bodyEl.dom.appendChild(panel.getEl().dom);
38860 if(panel.background !== true){
38861 this.setActivePanel(panel);
38863 this.fireEvent("paneladded", this, panel);
38870 this.initPanelAsTab(panel);
38874 if(panel.background !== true){
38875 this.tabs.activate(panel.getEl().id);
38877 this.fireEvent("paneladded", this, panel);
38882 * Hides the tab for the specified panel.
38883 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38885 hidePanel : function(panel){
38886 if(this.tabs && (panel = this.getPanel(panel))){
38887 this.tabs.hideTab(panel.getEl().id);
38892 * Unhides the tab for a previously hidden panel.
38893 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38895 unhidePanel : function(panel){
38896 if(this.tabs && (panel = this.getPanel(panel))){
38897 this.tabs.unhideTab(panel.getEl().id);
38901 clearPanels : function(){
38902 while(this.panels.getCount() > 0){
38903 this.remove(this.panels.first());
38908 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38909 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38910 * @param {Boolean} preservePanel Overrides the config preservePanel option
38911 * @return {Roo.ContentPanel} The panel that was removed
38913 remove : function(panel, preservePanel)
38915 panel = this.getPanel(panel);
38920 this.fireEvent("beforeremove", this, panel, e);
38921 if(e.cancel === true){
38924 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38925 var panelId = panel.getId();
38926 this.panels.removeKey(panelId);
38928 document.body.appendChild(panel.getEl().dom);
38931 this.tabs.removeTab(panel.getEl().id);
38932 }else if (!preservePanel){
38933 this.bodyEl.dom.removeChild(panel.getEl().dom);
38935 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38936 var p = this.panels.first();
38937 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38938 tempEl.appendChild(p.getEl().dom);
38939 this.bodyEl.update("");
38940 this.bodyEl.dom.appendChild(p.getEl().dom);
38942 this.updateTitle(p.getTitle());
38944 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38945 this.setActivePanel(p);
38947 panel.setRegion(null);
38948 if(this.activePanel == panel){
38949 this.activePanel = null;
38951 if(this.config.autoDestroy !== false && preservePanel !== true){
38952 try{panel.destroy();}catch(e){}
38954 this.fireEvent("panelremoved", this, panel);
38959 * Returns the TabPanel component used by this region
38960 * @return {Roo.TabPanel}
38962 getTabs : function(){
38966 createTool : function(parentEl, className){
38967 var btn = Roo.DomHelper.append(parentEl, {
38969 cls: "x-layout-tools-button",
38972 cls: "roo-layout-tools-button-inner " + className,
38976 btn.addClassOnOver("roo-layout-tools-button-over");
38981 * Ext JS Library 1.1.1
38982 * Copyright(c) 2006-2007, Ext JS, LLC.
38984 * Originally Released Under LGPL - original licence link has changed is not relivant.
38987 * <script type="text/javascript">
38993 * @class Roo.SplitLayoutRegion
38994 * @extends Roo.LayoutRegion
38995 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38997 Roo.bootstrap.layout.Split = function(config){
38998 this.cursor = config.cursor;
38999 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39002 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39004 splitTip : "Drag to resize.",
39005 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39006 useSplitTips : false,
39008 applyConfig : function(config){
39009 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39012 onRender : function(ctr,pos) {
39014 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39015 if(!this.config.split){
39020 var splitEl = Roo.DomHelper.append(ctr.dom, {
39022 id: this.el.id + "-split",
39023 cls: "roo-layout-split roo-layout-split-"+this.position,
39026 /** The SplitBar for this region
39027 * @type Roo.SplitBar */
39028 // does not exist yet...
39029 Roo.log([this.position, this.orientation]);
39031 this.split = new Roo.bootstrap.SplitBar({
39032 dragElement : splitEl,
39033 resizingElement: this.el,
39034 orientation : this.orientation
39037 this.split.on("moved", this.onSplitMove, this);
39038 this.split.useShim = this.config.useShim === true;
39039 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39040 if(this.useSplitTips){
39041 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39043 //if(config.collapsible){
39044 // this.split.el.on("dblclick", this.collapse, this);
39047 if(typeof this.config.minSize != "undefined"){
39048 this.split.minSize = this.config.minSize;
39050 if(typeof this.config.maxSize != "undefined"){
39051 this.split.maxSize = this.config.maxSize;
39053 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39054 this.hideSplitter();
39059 getHMaxSize : function(){
39060 var cmax = this.config.maxSize || 10000;
39061 var center = this.mgr.getRegion("center");
39062 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39065 getVMaxSize : function(){
39066 var cmax = this.config.maxSize || 10000;
39067 var center = this.mgr.getRegion("center");
39068 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39071 onSplitMove : function(split, newSize){
39072 this.fireEvent("resized", this, newSize);
39076 * Returns the {@link Roo.SplitBar} for this region.
39077 * @return {Roo.SplitBar}
39079 getSplitBar : function(){
39084 this.hideSplitter();
39085 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39088 hideSplitter : function(){
39090 this.split.el.setLocation(-2000,-2000);
39091 this.split.el.hide();
39097 this.split.el.show();
39099 Roo.bootstrap.layout.Split.superclass.show.call(this);
39102 beforeSlide: function(){
39103 if(Roo.isGecko){// firefox overflow auto bug workaround
39104 this.bodyEl.clip();
39106 this.tabs.bodyEl.clip();
39108 if(this.activePanel){
39109 this.activePanel.getEl().clip();
39111 if(this.activePanel.beforeSlide){
39112 this.activePanel.beforeSlide();
39118 afterSlide : function(){
39119 if(Roo.isGecko){// firefox overflow auto bug workaround
39120 this.bodyEl.unclip();
39122 this.tabs.bodyEl.unclip();
39124 if(this.activePanel){
39125 this.activePanel.getEl().unclip();
39126 if(this.activePanel.afterSlide){
39127 this.activePanel.afterSlide();
39133 initAutoHide : function(){
39134 if(this.autoHide !== false){
39135 if(!this.autoHideHd){
39136 var st = new Roo.util.DelayedTask(this.slideIn, this);
39137 this.autoHideHd = {
39138 "mouseout": function(e){
39139 if(!e.within(this.el, true)){
39143 "mouseover" : function(e){
39149 this.el.on(this.autoHideHd);
39153 clearAutoHide : function(){
39154 if(this.autoHide !== false){
39155 this.el.un("mouseout", this.autoHideHd.mouseout);
39156 this.el.un("mouseover", this.autoHideHd.mouseover);
39160 clearMonitor : function(){
39161 Roo.get(document).un("click", this.slideInIf, this);
39164 // these names are backwards but not changed for compat
39165 slideOut : function(){
39166 if(this.isSlid || this.el.hasActiveFx()){
39169 this.isSlid = true;
39170 if(this.collapseBtn){
39171 this.collapseBtn.hide();
39173 this.closeBtnState = this.closeBtn.getStyle('display');
39174 this.closeBtn.hide();
39176 this.stickBtn.show();
39179 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39180 this.beforeSlide();
39181 this.el.setStyle("z-index", 10001);
39182 this.el.slideIn(this.getSlideAnchor(), {
39183 callback: function(){
39185 this.initAutoHide();
39186 Roo.get(document).on("click", this.slideInIf, this);
39187 this.fireEvent("slideshow", this);
39194 afterSlideIn : function(){
39195 this.clearAutoHide();
39196 this.isSlid = false;
39197 this.clearMonitor();
39198 this.el.setStyle("z-index", "");
39199 if(this.collapseBtn){
39200 this.collapseBtn.show();
39202 this.closeBtn.setStyle('display', this.closeBtnState);
39204 this.stickBtn.hide();
39206 this.fireEvent("slidehide", this);
39209 slideIn : function(cb){
39210 if(!this.isSlid || this.el.hasActiveFx()){
39214 this.isSlid = false;
39215 this.beforeSlide();
39216 this.el.slideOut(this.getSlideAnchor(), {
39217 callback: function(){
39218 this.el.setLeftTop(-10000, -10000);
39220 this.afterSlideIn();
39228 slideInIf : function(e){
39229 if(!e.within(this.el)){
39234 animateCollapse : function(){
39235 this.beforeSlide();
39236 this.el.setStyle("z-index", 20000);
39237 var anchor = this.getSlideAnchor();
39238 this.el.slideOut(anchor, {
39239 callback : function(){
39240 this.el.setStyle("z-index", "");
39241 this.collapsedEl.slideIn(anchor, {duration:.3});
39243 this.el.setLocation(-10000,-10000);
39245 this.fireEvent("collapsed", this);
39252 animateExpand : function(){
39253 this.beforeSlide();
39254 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39255 this.el.setStyle("z-index", 20000);
39256 this.collapsedEl.hide({
39259 this.el.slideIn(this.getSlideAnchor(), {
39260 callback : function(){
39261 this.el.setStyle("z-index", "");
39264 this.split.el.show();
39266 this.fireEvent("invalidated", this);
39267 this.fireEvent("expanded", this);
39295 getAnchor : function(){
39296 return this.anchors[this.position];
39299 getCollapseAnchor : function(){
39300 return this.canchors[this.position];
39303 getSlideAnchor : function(){
39304 return this.sanchors[this.position];
39307 getAlignAdj : function(){
39308 var cm = this.cmargins;
39309 switch(this.position){
39325 getExpandAdj : function(){
39326 var c = this.collapsedEl, cm = this.cmargins;
39327 switch(this.position){
39329 return [-(cm.right+c.getWidth()+cm.left), 0];
39332 return [cm.right+c.getWidth()+cm.left, 0];
39335 return [0, -(cm.top+cm.bottom+c.getHeight())];
39338 return [0, cm.top+cm.bottom+c.getHeight()];
39344 * Ext JS Library 1.1.1
39345 * Copyright(c) 2006-2007, Ext JS, LLC.
39347 * Originally Released Under LGPL - original licence link has changed is not relivant.
39350 * <script type="text/javascript">
39353 * These classes are private internal classes
39355 Roo.bootstrap.layout.Center = function(config){
39356 config.region = "center";
39357 Roo.bootstrap.layout.Region.call(this, config);
39358 this.visible = true;
39359 this.minWidth = config.minWidth || 20;
39360 this.minHeight = config.minHeight || 20;
39363 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39365 // center panel can't be hidden
39369 // center panel can't be hidden
39372 getMinWidth: function(){
39373 return this.minWidth;
39376 getMinHeight: function(){
39377 return this.minHeight;
39391 Roo.bootstrap.layout.North = function(config)
39393 config.region = 'north';
39394 config.cursor = 'n-resize';
39396 Roo.bootstrap.layout.Split.call(this, config);
39400 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39401 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39402 this.split.el.addClass("roo-layout-split-v");
39404 //var size = config.initialSize || config.height;
39405 //if(this.el && typeof size != "undefined"){
39406 // this.el.setHeight(size);
39409 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39411 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39414 onRender : function(ctr, pos)
39416 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39417 var size = this.config.initialSize || this.config.height;
39418 if(this.el && typeof size != "undefined"){
39419 this.el.setHeight(size);
39424 getBox : function(){
39425 if(this.collapsed){
39426 return this.collapsedEl.getBox();
39428 var box = this.el.getBox();
39430 box.height += this.split.el.getHeight();
39435 updateBox : function(box){
39436 if(this.split && !this.collapsed){
39437 box.height -= this.split.el.getHeight();
39438 this.split.el.setLeft(box.x);
39439 this.split.el.setTop(box.y+box.height);
39440 this.split.el.setWidth(box.width);
39442 if(this.collapsed){
39443 this.updateBody(box.width, null);
39445 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39453 Roo.bootstrap.layout.South = function(config){
39454 config.region = 'south';
39455 config.cursor = 's-resize';
39456 Roo.bootstrap.layout.Split.call(this, config);
39458 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39459 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39460 this.split.el.addClass("roo-layout-split-v");
39465 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39466 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39468 onRender : function(ctr, pos)
39470 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39471 var size = this.config.initialSize || this.config.height;
39472 if(this.el && typeof size != "undefined"){
39473 this.el.setHeight(size);
39478 getBox : function(){
39479 if(this.collapsed){
39480 return this.collapsedEl.getBox();
39482 var box = this.el.getBox();
39484 var sh = this.split.el.getHeight();
39491 updateBox : function(box){
39492 if(this.split && !this.collapsed){
39493 var sh = this.split.el.getHeight();
39496 this.split.el.setLeft(box.x);
39497 this.split.el.setTop(box.y-sh);
39498 this.split.el.setWidth(box.width);
39500 if(this.collapsed){
39501 this.updateBody(box.width, null);
39503 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39507 Roo.bootstrap.layout.East = function(config){
39508 config.region = "east";
39509 config.cursor = "e-resize";
39510 Roo.bootstrap.layout.Split.call(this, config);
39512 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39513 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39514 this.split.el.addClass("roo-layout-split-h");
39518 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39519 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39521 onRender : function(ctr, pos)
39523 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39524 var size = this.config.initialSize || this.config.width;
39525 if(this.el && typeof size != "undefined"){
39526 this.el.setWidth(size);
39531 getBox : function(){
39532 if(this.collapsed){
39533 return this.collapsedEl.getBox();
39535 var box = this.el.getBox();
39537 var sw = this.split.el.getWidth();
39544 updateBox : function(box){
39545 if(this.split && !this.collapsed){
39546 var sw = this.split.el.getWidth();
39548 this.split.el.setLeft(box.x);
39549 this.split.el.setTop(box.y);
39550 this.split.el.setHeight(box.height);
39553 if(this.collapsed){
39554 this.updateBody(null, box.height);
39556 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39560 Roo.bootstrap.layout.West = function(config){
39561 config.region = "west";
39562 config.cursor = "w-resize";
39564 Roo.bootstrap.layout.Split.call(this, config);
39566 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39567 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39568 this.split.el.addClass("roo-layout-split-h");
39572 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39573 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39575 onRender: function(ctr, pos)
39577 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39578 var size = this.config.initialSize || this.config.width;
39579 if(typeof size != "undefined"){
39580 this.el.setWidth(size);
39584 getBox : function(){
39585 if(this.collapsed){
39586 return this.collapsedEl.getBox();
39588 var box = this.el.getBox();
39589 if (box.width == 0) {
39590 box.width = this.config.width; // kludge?
39593 box.width += this.split.el.getWidth();
39598 updateBox : function(box){
39599 if(this.split && !this.collapsed){
39600 var sw = this.split.el.getWidth();
39602 this.split.el.setLeft(box.x+box.width);
39603 this.split.el.setTop(box.y);
39604 this.split.el.setHeight(box.height);
39606 if(this.collapsed){
39607 this.updateBody(null, box.height);
39609 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39611 });Roo.namespace("Roo.bootstrap.panel");/*
39613 * Ext JS Library 1.1.1
39614 * Copyright(c) 2006-2007, Ext JS, LLC.
39616 * Originally Released Under LGPL - original licence link has changed is not relivant.
39619 * <script type="text/javascript">
39622 * @class Roo.ContentPanel
39623 * @extends Roo.util.Observable
39624 * A basic ContentPanel element.
39625 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39626 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39627 * @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
39628 * @cfg {Boolean} closable True if the panel can be closed/removed
39629 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39630 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39631 * @cfg {Toolbar} toolbar A toolbar for this panel
39632 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39633 * @cfg {String} title The title for this panel
39634 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39635 * @cfg {String} url Calls {@link #setUrl} with this value
39636 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39637 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39638 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39639 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39640 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39641 * @cfg {Boolean} badges render the badges
39642 * @cfg {String} cls extra classes to use
39643 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39646 * Create a new ContentPanel.
39647 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39648 * @param {String/Object} config A string to set only the title or a config object
39649 * @param {String} content (optional) Set the HTML content for this panel
39650 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39652 Roo.bootstrap.panel.Content = function( config){
39654 this.tpl = config.tpl || false;
39656 var el = config.el;
39657 var content = config.content;
39659 if(config.autoCreate){ // xtype is available if this is called from factory
39662 this.el = Roo.get(el);
39663 if(!this.el && config && config.autoCreate){
39664 if(typeof config.autoCreate == "object"){
39665 if(!config.autoCreate.id){
39666 config.autoCreate.id = config.id||el;
39668 this.el = Roo.DomHelper.append(document.body,
39669 config.autoCreate, true);
39673 cls: (config.cls || '') +
39674 (config.background ? ' bg-' + config.background : '') +
39675 " roo-layout-inactive-content",
39678 if (config.iframe) {
39682 style : 'border: 0px',
39683 src : 'about:blank'
39689 elcfg.html = config.html;
39693 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39694 if (config.iframe) {
39695 this.iframeEl = this.el.select('iframe',true).first();
39700 this.closable = false;
39701 this.loaded = false;
39702 this.active = false;
39705 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39707 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39709 this.wrapEl = this.el; //this.el.wrap();
39711 if (config.toolbar.items) {
39712 ti = config.toolbar.items ;
39713 delete config.toolbar.items ;
39717 this.toolbar.render(this.wrapEl, 'before');
39718 for(var i =0;i < ti.length;i++) {
39719 // Roo.log(['add child', items[i]]);
39720 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39722 this.toolbar.items = nitems;
39723 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39724 delete config.toolbar;
39728 // xtype created footer. - not sure if will work as we normally have to render first..
39729 if (this.footer && !this.footer.el && this.footer.xtype) {
39730 if (!this.wrapEl) {
39731 this.wrapEl = this.el.wrap();
39734 this.footer.container = this.wrapEl.createChild();
39736 this.footer = Roo.factory(this.footer, Roo);
39741 if(typeof config == "string"){
39742 this.title = config;
39744 Roo.apply(this, config);
39748 this.resizeEl = Roo.get(this.resizeEl, true);
39750 this.resizeEl = this.el;
39752 // handle view.xtype
39760 * Fires when this panel is activated.
39761 * @param {Roo.ContentPanel} this
39765 * @event deactivate
39766 * Fires when this panel is activated.
39767 * @param {Roo.ContentPanel} this
39769 "deactivate" : true,
39773 * Fires when this panel is resized if fitToFrame is true.
39774 * @param {Roo.ContentPanel} this
39775 * @param {Number} width The width after any component adjustments
39776 * @param {Number} height The height after any component adjustments
39782 * Fires when this tab is created
39783 * @param {Roo.ContentPanel} this
39794 if(this.autoScroll && !this.iframe){
39795 this.resizeEl.setStyle("overflow", "auto");
39797 // fix randome scrolling
39798 //this.el.on('scroll', function() {
39799 // Roo.log('fix random scolling');
39800 // this.scrollTo('top',0);
39803 content = content || this.content;
39805 this.setContent(content);
39807 if(config && config.url){
39808 this.setUrl(this.url, this.params, this.loadOnce);
39813 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39815 if (this.view && typeof(this.view.xtype) != 'undefined') {
39816 this.view.el = this.el.appendChild(document.createElement("div"));
39817 this.view = Roo.factory(this.view);
39818 this.view.render && this.view.render(false, '');
39822 this.fireEvent('render', this);
39825 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39835 setRegion : function(region){
39836 this.region = region;
39837 this.setActiveClass(region && !this.background);
39841 setActiveClass: function(state)
39844 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39845 this.el.setStyle('position','relative');
39847 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39848 this.el.setStyle('position', 'absolute');
39853 * Returns the toolbar for this Panel if one was configured.
39854 * @return {Roo.Toolbar}
39856 getToolbar : function(){
39857 return this.toolbar;
39860 setActiveState : function(active)
39862 this.active = active;
39863 this.setActiveClass(active);
39865 if(this.fireEvent("deactivate", this) === false){
39870 this.fireEvent("activate", this);
39874 * Updates this panel's element (not for iframe)
39875 * @param {String} content The new content
39876 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39878 setContent : function(content, loadScripts){
39883 this.el.update(content, loadScripts);
39886 ignoreResize : function(w, h){
39887 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39890 this.lastSize = {width: w, height: h};
39895 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39896 * @return {Roo.UpdateManager} The UpdateManager
39898 getUpdateManager : function(){
39902 return this.el.getUpdateManager();
39905 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39906 * Does not work with IFRAME contents
39907 * @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:
39910 url: "your-url.php",
39911 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39912 callback: yourFunction,
39913 scope: yourObject, //(optional scope)
39916 text: "Loading...",
39922 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39923 * 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.
39924 * @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}
39925 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39926 * @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.
39927 * @return {Roo.ContentPanel} this
39935 var um = this.el.getUpdateManager();
39936 um.update.apply(um, arguments);
39942 * 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.
39943 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39944 * @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)
39945 * @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)
39946 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39948 setUrl : function(url, params, loadOnce){
39950 this.iframeEl.dom.src = url;
39954 if(this.refreshDelegate){
39955 this.removeListener("activate", this.refreshDelegate);
39957 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39958 this.on("activate", this.refreshDelegate);
39959 return this.el.getUpdateManager();
39962 _handleRefresh : function(url, params, loadOnce){
39963 if(!loadOnce || !this.loaded){
39964 var updater = this.el.getUpdateManager();
39965 updater.update(url, params, this._setLoaded.createDelegate(this));
39969 _setLoaded : function(){
39970 this.loaded = true;
39974 * Returns this panel's id
39977 getId : function(){
39982 * Returns this panel's element - used by regiosn to add.
39983 * @return {Roo.Element}
39985 getEl : function(){
39986 return this.wrapEl || this.el;
39991 adjustForComponents : function(width, height)
39993 //Roo.log('adjustForComponents ');
39994 if(this.resizeEl != this.el){
39995 width -= this.el.getFrameWidth('lr');
39996 height -= this.el.getFrameWidth('tb');
39999 var te = this.toolbar.getEl();
40000 te.setWidth(width);
40001 height -= te.getHeight();
40004 var te = this.footer.getEl();
40005 te.setWidth(width);
40006 height -= te.getHeight();
40010 if(this.adjustments){
40011 width += this.adjustments[0];
40012 height += this.adjustments[1];
40014 return {"width": width, "height": height};
40017 setSize : function(width, height){
40018 if(this.fitToFrame && !this.ignoreResize(width, height)){
40019 if(this.fitContainer && this.resizeEl != this.el){
40020 this.el.setSize(width, height);
40022 var size = this.adjustForComponents(width, height);
40024 this.iframeEl.setSize(width,height);
40027 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40028 this.fireEvent('resize', this, size.width, size.height);
40035 * Returns this panel's title
40038 getTitle : function(){
40040 if (typeof(this.title) != 'object') {
40045 for (var k in this.title) {
40046 if (!this.title.hasOwnProperty(k)) {
40050 if (k.indexOf('-') >= 0) {
40051 var s = k.split('-');
40052 for (var i = 0; i<s.length; i++) {
40053 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40056 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40063 * Set this panel's title
40064 * @param {String} title
40066 setTitle : function(title){
40067 this.title = title;
40069 this.region.updatePanelTitle(this, title);
40074 * Returns true is this panel was configured to be closable
40075 * @return {Boolean}
40077 isClosable : function(){
40078 return this.closable;
40081 beforeSlide : function(){
40083 this.resizeEl.clip();
40086 afterSlide : function(){
40088 this.resizeEl.unclip();
40092 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40093 * Will fail silently if the {@link #setUrl} method has not been called.
40094 * This does not activate the panel, just updates its content.
40096 refresh : function(){
40097 if(this.refreshDelegate){
40098 this.loaded = false;
40099 this.refreshDelegate();
40104 * Destroys this panel
40106 destroy : function(){
40107 this.el.removeAllListeners();
40108 var tempEl = document.createElement("span");
40109 tempEl.appendChild(this.el.dom);
40110 tempEl.innerHTML = "";
40116 * form - if the content panel contains a form - this is a reference to it.
40117 * @type {Roo.form.Form}
40121 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40122 * This contains a reference to it.
40128 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40138 * @param {Object} cfg Xtype definition of item to add.
40142 getChildContainer: function () {
40143 return this.getEl();
40148 var ret = new Roo.factory(cfg);
40153 if (cfg.xtype.match(/^Form$/)) {
40156 //if (this.footer) {
40157 // el = this.footer.container.insertSibling(false, 'before');
40159 el = this.el.createChild();
40162 this.form = new Roo.form.Form(cfg);
40165 if ( this.form.allItems.length) {
40166 this.form.render(el.dom);
40170 // should only have one of theses..
40171 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40172 // views.. should not be just added - used named prop 'view''
40174 cfg.el = this.el.appendChild(document.createElement("div"));
40177 var ret = new Roo.factory(cfg);
40179 ret.render && ret.render(false, ''); // render blank..
40189 * @class Roo.bootstrap.panel.Grid
40190 * @extends Roo.bootstrap.panel.Content
40192 * Create a new GridPanel.
40193 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40194 * @param {Object} config A the config object
40200 Roo.bootstrap.panel.Grid = function(config)
40204 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40205 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40207 config.el = this.wrapper;
40208 //this.el = this.wrapper;
40210 if (config.container) {
40211 // ctor'ed from a Border/panel.grid
40214 this.wrapper.setStyle("overflow", "hidden");
40215 this.wrapper.addClass('roo-grid-container');
40220 if(config.toolbar){
40221 var tool_el = this.wrapper.createChild();
40222 this.toolbar = Roo.factory(config.toolbar);
40224 if (config.toolbar.items) {
40225 ti = config.toolbar.items ;
40226 delete config.toolbar.items ;
40230 this.toolbar.render(tool_el);
40231 for(var i =0;i < ti.length;i++) {
40232 // Roo.log(['add child', items[i]]);
40233 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40235 this.toolbar.items = nitems;
40237 delete config.toolbar;
40240 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40241 config.grid.scrollBody = true;;
40242 config.grid.monitorWindowResize = false; // turn off autosizing
40243 config.grid.autoHeight = false;
40244 config.grid.autoWidth = false;
40246 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40248 if (config.background) {
40249 // render grid on panel activation (if panel background)
40250 this.on('activate', function(gp) {
40251 if (!gp.grid.rendered) {
40252 gp.grid.render(this.wrapper);
40253 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40258 this.grid.render(this.wrapper);
40259 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40262 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40263 // ??? needed ??? config.el = this.wrapper;
40268 // xtype created footer. - not sure if will work as we normally have to render first..
40269 if (this.footer && !this.footer.el && this.footer.xtype) {
40271 var ctr = this.grid.getView().getFooterPanel(true);
40272 this.footer.dataSource = this.grid.dataSource;
40273 this.footer = Roo.factory(this.footer, Roo);
40274 this.footer.render(ctr);
40284 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40285 getId : function(){
40286 return this.grid.id;
40290 * Returns the grid for this panel
40291 * @return {Roo.bootstrap.Table}
40293 getGrid : function(){
40297 setSize : function(width, height){
40298 if(!this.ignoreResize(width, height)){
40299 var grid = this.grid;
40300 var size = this.adjustForComponents(width, height);
40301 // tfoot is not a footer?
40304 var gridel = grid.getGridEl();
40305 gridel.setSize(size.width, size.height);
40307 var tbd = grid.getGridEl().select('tbody', true).first();
40308 var thd = grid.getGridEl().select('thead',true).first();
40309 var tbf= grid.getGridEl().select('tfoot', true).first();
40312 size.height -= tbf.getHeight();
40315 size.height -= thd.getHeight();
40318 tbd.setSize(size.width, size.height );
40319 // this is for the account management tab -seems to work there.
40320 var thd = grid.getGridEl().select('thead',true).first();
40322 // tbd.setSize(size.width, size.height - thd.getHeight());
40331 beforeSlide : function(){
40332 this.grid.getView().scroller.clip();
40335 afterSlide : function(){
40336 this.grid.getView().scroller.unclip();
40339 destroy : function(){
40340 this.grid.destroy();
40342 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40347 * @class Roo.bootstrap.panel.Nest
40348 * @extends Roo.bootstrap.panel.Content
40350 * Create a new Panel, that can contain a layout.Border.
40353 * @param {Roo.BorderLayout} layout The layout for this panel
40354 * @param {String/Object} config A string to set only the title or a config object
40356 Roo.bootstrap.panel.Nest = function(config)
40358 // construct with only one argument..
40359 /* FIXME - implement nicer consturctors
40360 if (layout.layout) {
40362 layout = config.layout;
40363 delete config.layout;
40365 if (layout.xtype && !layout.getEl) {
40366 // then layout needs constructing..
40367 layout = Roo.factory(layout, Roo);
40371 config.el = config.layout.getEl();
40373 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40375 config.layout.monitorWindowResize = false; // turn off autosizing
40376 this.layout = config.layout;
40377 this.layout.getEl().addClass("roo-layout-nested-layout");
40378 this.layout.parent = this;
40385 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40387 setSize : function(width, height){
40388 if(!this.ignoreResize(width, height)){
40389 var size = this.adjustForComponents(width, height);
40390 var el = this.layout.getEl();
40391 if (size.height < 1) {
40392 el.setWidth(size.width);
40394 el.setSize(size.width, size.height);
40396 var touch = el.dom.offsetWidth;
40397 this.layout.layout();
40398 // ie requires a double layout on the first pass
40399 if(Roo.isIE && !this.initialized){
40400 this.initialized = true;
40401 this.layout.layout();
40406 // activate all subpanels if not currently active..
40408 setActiveState : function(active){
40409 this.active = active;
40410 this.setActiveClass(active);
40413 this.fireEvent("deactivate", this);
40417 this.fireEvent("activate", this);
40418 // not sure if this should happen before or after..
40419 if (!this.layout) {
40420 return; // should not happen..
40423 for (var r in this.layout.regions) {
40424 reg = this.layout.getRegion(r);
40425 if (reg.getActivePanel()) {
40426 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40427 reg.setActivePanel(reg.getActivePanel());
40430 if (!reg.panels.length) {
40433 reg.showPanel(reg.getPanel(0));
40442 * Returns the nested BorderLayout for this panel
40443 * @return {Roo.BorderLayout}
40445 getLayout : function(){
40446 return this.layout;
40450 * Adds a xtype elements to the layout of the nested panel
40454 xtype : 'ContentPanel',
40461 xtype : 'NestedLayoutPanel',
40467 items : [ ... list of content panels or nested layout panels.. ]
40471 * @param {Object} cfg Xtype definition of item to add.
40473 addxtype : function(cfg) {
40474 return this.layout.addxtype(cfg);
40479 * Ext JS Library 1.1.1
40480 * Copyright(c) 2006-2007, Ext JS, LLC.
40482 * Originally Released Under LGPL - original licence link has changed is not relivant.
40485 * <script type="text/javascript">
40488 * @class Roo.TabPanel
40489 * @extends Roo.util.Observable
40490 * A lightweight tab container.
40494 // basic tabs 1, built from existing content
40495 var tabs = new Roo.TabPanel("tabs1");
40496 tabs.addTab("script", "View Script");
40497 tabs.addTab("markup", "View Markup");
40498 tabs.activate("script");
40500 // more advanced tabs, built from javascript
40501 var jtabs = new Roo.TabPanel("jtabs");
40502 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40504 // set up the UpdateManager
40505 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40506 var updater = tab2.getUpdateManager();
40507 updater.setDefaultUrl("ajax1.htm");
40508 tab2.on('activate', updater.refresh, updater, true);
40510 // Use setUrl for Ajax loading
40511 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40512 tab3.setUrl("ajax2.htm", null, true);
40515 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40518 jtabs.activate("jtabs-1");
40521 * Create a new TabPanel.
40522 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40523 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40525 Roo.bootstrap.panel.Tabs = function(config){
40527 * The container element for this TabPanel.
40528 * @type Roo.Element
40530 this.el = Roo.get(config.el);
40533 if(typeof config == "boolean"){
40534 this.tabPosition = config ? "bottom" : "top";
40536 Roo.apply(this, config);
40540 if(this.tabPosition == "bottom"){
40541 // if tabs are at the bottom = create the body first.
40542 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40543 this.el.addClass("roo-tabs-bottom");
40545 // next create the tabs holders
40547 if (this.tabPosition == "west"){
40549 var reg = this.region; // fake it..
40551 if (!reg.mgr.parent) {
40554 reg = reg.mgr.parent.region;
40556 Roo.log("got nest?");
40558 if (reg.mgr.getRegion('west')) {
40559 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40560 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40561 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40562 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40563 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40571 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40572 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40573 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40574 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40579 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40582 // finally - if tabs are at the top, then create the body last..
40583 if(this.tabPosition != "bottom"){
40584 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40585 * @type Roo.Element
40587 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40588 this.el.addClass("roo-tabs-top");
40592 this.bodyEl.setStyle("position", "relative");
40594 this.active = null;
40595 this.activateDelegate = this.activate.createDelegate(this);
40600 * Fires when the active tab changes
40601 * @param {Roo.TabPanel} this
40602 * @param {Roo.TabPanelItem} activePanel The new active tab
40606 * @event beforetabchange
40607 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40608 * @param {Roo.TabPanel} this
40609 * @param {Object} e Set cancel to true on this object to cancel the tab change
40610 * @param {Roo.TabPanelItem} tab The tab being changed to
40612 "beforetabchange" : true
40615 Roo.EventManager.onWindowResize(this.onResize, this);
40616 this.cpad = this.el.getPadding("lr");
40617 this.hiddenCount = 0;
40620 // toolbar on the tabbar support...
40621 if (this.toolbar) {
40622 alert("no toolbar support yet");
40623 this.toolbar = false;
40625 var tcfg = this.toolbar;
40626 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40627 this.toolbar = new Roo.Toolbar(tcfg);
40628 if (Roo.isSafari) {
40629 var tbl = tcfg.container.child('table', true);
40630 tbl.setAttribute('width', '100%');
40638 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40641 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40643 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40645 tabPosition : "top",
40647 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40649 currentTabWidth : 0,
40651 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40655 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40659 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40661 preferredTabWidth : 175,
40663 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40665 resizeTabs : false,
40667 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40669 monitorResize : true,
40671 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40673 toolbar : false, // set by caller..
40675 region : false, /// set by caller
40677 disableTooltips : true, // not used yet...
40680 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40681 * @param {String} id The id of the div to use <b>or create</b>
40682 * @param {String} text The text for the tab
40683 * @param {String} content (optional) Content to put in the TabPanelItem body
40684 * @param {Boolean} closable (optional) True to create a close icon on the tab
40685 * @return {Roo.TabPanelItem} The created TabPanelItem
40687 addTab : function(id, text, content, closable, tpl)
40689 var item = new Roo.bootstrap.panel.TabItem({
40693 closable : closable,
40696 this.addTabItem(item);
40698 item.setContent(content);
40704 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40705 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40706 * @return {Roo.TabPanelItem}
40708 getTab : function(id){
40709 return this.items[id];
40713 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40714 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40716 hideTab : function(id){
40717 var t = this.items[id];
40720 this.hiddenCount++;
40721 this.autoSizeTabs();
40726 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40727 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40729 unhideTab : function(id){
40730 var t = this.items[id];
40732 t.setHidden(false);
40733 this.hiddenCount--;
40734 this.autoSizeTabs();
40739 * Adds an existing {@link Roo.TabPanelItem}.
40740 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40742 addTabItem : function(item)
40744 this.items[item.id] = item;
40745 this.items.push(item);
40746 this.autoSizeTabs();
40747 // if(this.resizeTabs){
40748 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40749 // this.autoSizeTabs();
40751 // item.autoSize();
40756 * Removes a {@link Roo.TabPanelItem}.
40757 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40759 removeTab : function(id){
40760 var items = this.items;
40761 var tab = items[id];
40762 if(!tab) { return; }
40763 var index = items.indexOf(tab);
40764 if(this.active == tab && items.length > 1){
40765 var newTab = this.getNextAvailable(index);
40770 this.stripEl.dom.removeChild(tab.pnode.dom);
40771 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40772 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40774 items.splice(index, 1);
40775 delete this.items[tab.id];
40776 tab.fireEvent("close", tab);
40777 tab.purgeListeners();
40778 this.autoSizeTabs();
40781 getNextAvailable : function(start){
40782 var items = this.items;
40784 // look for a next tab that will slide over to
40785 // replace the one being removed
40786 while(index < items.length){
40787 var item = items[++index];
40788 if(item && !item.isHidden()){
40792 // if one isn't found select the previous tab (on the left)
40795 var item = items[--index];
40796 if(item && !item.isHidden()){
40804 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40805 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40807 disableTab : function(id){
40808 var tab = this.items[id];
40809 if(tab && this.active != tab){
40815 * Enables a {@link Roo.TabPanelItem} that is disabled.
40816 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40818 enableTab : function(id){
40819 var tab = this.items[id];
40824 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40825 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40826 * @return {Roo.TabPanelItem} The TabPanelItem.
40828 activate : function(id)
40830 //Roo.log('activite:' + id);
40832 var tab = this.items[id];
40836 if(tab == this.active || tab.disabled){
40840 this.fireEvent("beforetabchange", this, e, tab);
40841 if(e.cancel !== true && !tab.disabled){
40843 this.active.hide();
40845 this.active = this.items[id];
40846 this.active.show();
40847 this.fireEvent("tabchange", this, this.active);
40853 * Gets the active {@link Roo.TabPanelItem}.
40854 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40856 getActiveTab : function(){
40857 return this.active;
40861 * Updates the tab body element to fit the height of the container element
40862 * for overflow scrolling
40863 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40865 syncHeight : function(targetHeight){
40866 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40867 var bm = this.bodyEl.getMargins();
40868 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40869 this.bodyEl.setHeight(newHeight);
40873 onResize : function(){
40874 if(this.monitorResize){
40875 this.autoSizeTabs();
40880 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40882 beginUpdate : function(){
40883 this.updating = true;
40887 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40889 endUpdate : function(){
40890 this.updating = false;
40891 this.autoSizeTabs();
40895 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40897 autoSizeTabs : function()
40899 var count = this.items.length;
40900 var vcount = count - this.hiddenCount;
40903 this.stripEl.hide();
40905 this.stripEl.show();
40908 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40913 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40914 var availWidth = Math.floor(w / vcount);
40915 var b = this.stripBody;
40916 if(b.getWidth() > w){
40917 var tabs = this.items;
40918 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40919 if(availWidth < this.minTabWidth){
40920 /*if(!this.sleft){ // incomplete scrolling code
40921 this.createScrollButtons();
40924 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40927 if(this.currentTabWidth < this.preferredTabWidth){
40928 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40934 * Returns the number of tabs in this TabPanel.
40937 getCount : function(){
40938 return this.items.length;
40942 * Resizes all the tabs to the passed width
40943 * @param {Number} The new width
40945 setTabWidth : function(width){
40946 this.currentTabWidth = width;
40947 for(var i = 0, len = this.items.length; i < len; i++) {
40948 if(!this.items[i].isHidden()) {
40949 this.items[i].setWidth(width);
40955 * Destroys this TabPanel
40956 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40958 destroy : function(removeEl){
40959 Roo.EventManager.removeResizeListener(this.onResize, this);
40960 for(var i = 0, len = this.items.length; i < len; i++){
40961 this.items[i].purgeListeners();
40963 if(removeEl === true){
40964 this.el.update("");
40969 createStrip : function(container)
40971 var strip = document.createElement("nav");
40972 strip.className = Roo.bootstrap.version == 4 ?
40973 "navbar-light bg-light" :
40974 "navbar navbar-default"; //"x-tabs-wrap";
40975 container.appendChild(strip);
40979 createStripList : function(strip)
40981 // div wrapper for retard IE
40982 // returns the "tr" element.
40983 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40984 //'<div class="x-tabs-strip-wrap">'+
40985 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40986 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40987 return strip.firstChild; //.firstChild.firstChild.firstChild;
40989 createBody : function(container)
40991 var body = document.createElement("div");
40992 Roo.id(body, "tab-body");
40993 //Roo.fly(body).addClass("x-tabs-body");
40994 Roo.fly(body).addClass("tab-content");
40995 container.appendChild(body);
40998 createItemBody :function(bodyEl, id){
40999 var body = Roo.getDom(id);
41001 body = document.createElement("div");
41004 //Roo.fly(body).addClass("x-tabs-item-body");
41005 Roo.fly(body).addClass("tab-pane");
41006 bodyEl.insertBefore(body, bodyEl.firstChild);
41010 createStripElements : function(stripEl, text, closable, tpl)
41012 var td = document.createElement("li"); // was td..
41013 td.className = 'nav-item';
41015 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41018 stripEl.appendChild(td);
41020 td.className = "x-tabs-closable";
41021 if(!this.closeTpl){
41022 this.closeTpl = new Roo.Template(
41023 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41024 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41025 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41028 var el = this.closeTpl.overwrite(td, {"text": text});
41029 var close = el.getElementsByTagName("div")[0];
41030 var inner = el.getElementsByTagName("em")[0];
41031 return {"el": el, "close": close, "inner": inner};
41034 // not sure what this is..
41035 // if(!this.tabTpl){
41036 //this.tabTpl = new Roo.Template(
41037 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41038 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41040 // this.tabTpl = new Roo.Template(
41041 // '<a href="#">' +
41042 // '<span unselectable="on"' +
41043 // (this.disableTooltips ? '' : ' title="{text}"') +
41044 // ' >{text}</span></a>'
41050 var template = tpl || this.tabTpl || false;
41053 template = new Roo.Template(
41054 Roo.bootstrap.version == 4 ?
41056 '<a class="nav-link" href="#" unselectable="on"' +
41057 (this.disableTooltips ? '' : ' title="{text}"') +
41060 '<a class="nav-link" href="#">' +
41061 '<span unselectable="on"' +
41062 (this.disableTooltips ? '' : ' title="{text}"') +
41063 ' >{text}</span></a>'
41068 switch (typeof(template)) {
41072 template = new Roo.Template(template);
41078 var el = template.overwrite(td, {"text": text});
41080 var inner = el.getElementsByTagName("span")[0];
41082 return {"el": el, "inner": inner};
41090 * @class Roo.TabPanelItem
41091 * @extends Roo.util.Observable
41092 * Represents an individual item (tab plus body) in a TabPanel.
41093 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41094 * @param {String} id The id of this TabPanelItem
41095 * @param {String} text The text for the tab of this TabPanelItem
41096 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41098 Roo.bootstrap.panel.TabItem = function(config){
41100 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41101 * @type Roo.TabPanel
41103 this.tabPanel = config.panel;
41105 * The id for this TabPanelItem
41108 this.id = config.id;
41110 this.disabled = false;
41112 this.text = config.text;
41114 this.loaded = false;
41115 this.closable = config.closable;
41118 * The body element for this TabPanelItem.
41119 * @type Roo.Element
41121 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41122 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41123 this.bodyEl.setStyle("display", "block");
41124 this.bodyEl.setStyle("zoom", "1");
41125 //this.hideAction();
41127 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41129 this.el = Roo.get(els.el);
41130 this.inner = Roo.get(els.inner, true);
41131 this.textEl = Roo.bootstrap.version == 4 ?
41132 this.el : Roo.get(this.el.dom.firstChild, true);
41134 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41135 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41138 // this.el.on("mousedown", this.onTabMouseDown, this);
41139 this.el.on("click", this.onTabClick, this);
41141 if(config.closable){
41142 var c = Roo.get(els.close, true);
41143 c.dom.title = this.closeText;
41144 c.addClassOnOver("close-over");
41145 c.on("click", this.closeClick, this);
41151 * Fires when this tab becomes the active tab.
41152 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41153 * @param {Roo.TabPanelItem} this
41157 * @event beforeclose
41158 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41159 * @param {Roo.TabPanelItem} this
41160 * @param {Object} e Set cancel to true on this object to cancel the close.
41162 "beforeclose": true,
41165 * Fires when this tab is closed.
41166 * @param {Roo.TabPanelItem} this
41170 * @event deactivate
41171 * Fires when this tab is no longer the active tab.
41172 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41173 * @param {Roo.TabPanelItem} this
41175 "deactivate" : true
41177 this.hidden = false;
41179 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41182 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41184 purgeListeners : function(){
41185 Roo.util.Observable.prototype.purgeListeners.call(this);
41186 this.el.removeAllListeners();
41189 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41192 this.status_node.addClass("active");
41195 this.tabPanel.stripWrap.repaint();
41197 this.fireEvent("activate", this.tabPanel, this);
41201 * Returns true if this tab is the active tab.
41202 * @return {Boolean}
41204 isActive : function(){
41205 return this.tabPanel.getActiveTab() == this;
41209 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41212 this.status_node.removeClass("active");
41214 this.fireEvent("deactivate", this.tabPanel, this);
41217 hideAction : function(){
41218 this.bodyEl.hide();
41219 this.bodyEl.setStyle("position", "absolute");
41220 this.bodyEl.setLeft("-20000px");
41221 this.bodyEl.setTop("-20000px");
41224 showAction : function(){
41225 this.bodyEl.setStyle("position", "relative");
41226 this.bodyEl.setTop("");
41227 this.bodyEl.setLeft("");
41228 this.bodyEl.show();
41232 * Set the tooltip for the tab.
41233 * @param {String} tooltip The tab's tooltip
41235 setTooltip : function(text){
41236 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41237 this.textEl.dom.qtip = text;
41238 this.textEl.dom.removeAttribute('title');
41240 this.textEl.dom.title = text;
41244 onTabClick : function(e){
41245 e.preventDefault();
41246 this.tabPanel.activate(this.id);
41249 onTabMouseDown : function(e){
41250 e.preventDefault();
41251 this.tabPanel.activate(this.id);
41254 getWidth : function(){
41255 return this.inner.getWidth();
41258 setWidth : function(width){
41259 var iwidth = width - this.linode.getPadding("lr");
41260 this.inner.setWidth(iwidth);
41261 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41262 this.linode.setWidth(width);
41266 * Show or hide the tab
41267 * @param {Boolean} hidden True to hide or false to show.
41269 setHidden : function(hidden){
41270 this.hidden = hidden;
41271 this.linode.setStyle("display", hidden ? "none" : "");
41275 * Returns true if this tab is "hidden"
41276 * @return {Boolean}
41278 isHidden : function(){
41279 return this.hidden;
41283 * Returns the text for this tab
41286 getText : function(){
41290 autoSize : function(){
41291 //this.el.beginMeasure();
41292 this.textEl.setWidth(1);
41294 * #2804 [new] Tabs in Roojs
41295 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41297 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41298 //this.el.endMeasure();
41302 * Sets the text for the tab (Note: this also sets the tooltip text)
41303 * @param {String} text The tab's text and tooltip
41305 setText : function(text){
41307 this.textEl.update(text);
41308 this.setTooltip(text);
41309 //if(!this.tabPanel.resizeTabs){
41310 // this.autoSize();
41314 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41316 activate : function(){
41317 this.tabPanel.activate(this.id);
41321 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41323 disable : function(){
41324 if(this.tabPanel.active != this){
41325 this.disabled = true;
41326 this.status_node.addClass("disabled");
41331 * Enables this TabPanelItem if it was previously disabled.
41333 enable : function(){
41334 this.disabled = false;
41335 this.status_node.removeClass("disabled");
41339 * Sets the content for this TabPanelItem.
41340 * @param {String} content The content
41341 * @param {Boolean} loadScripts true to look for and load scripts
41343 setContent : function(content, loadScripts){
41344 this.bodyEl.update(content, loadScripts);
41348 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41349 * @return {Roo.UpdateManager} The UpdateManager
41351 getUpdateManager : function(){
41352 return this.bodyEl.getUpdateManager();
41356 * Set a URL to be used to load the content for this TabPanelItem.
41357 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41358 * @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)
41359 * @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)
41360 * @return {Roo.UpdateManager} The UpdateManager
41362 setUrl : function(url, params, loadOnce){
41363 if(this.refreshDelegate){
41364 this.un('activate', this.refreshDelegate);
41366 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41367 this.on("activate", this.refreshDelegate);
41368 return this.bodyEl.getUpdateManager();
41372 _handleRefresh : function(url, params, loadOnce){
41373 if(!loadOnce || !this.loaded){
41374 var updater = this.bodyEl.getUpdateManager();
41375 updater.update(url, params, this._setLoaded.createDelegate(this));
41380 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41381 * Will fail silently if the setUrl method has not been called.
41382 * This does not activate the panel, just updates its content.
41384 refresh : function(){
41385 if(this.refreshDelegate){
41386 this.loaded = false;
41387 this.refreshDelegate();
41392 _setLoaded : function(){
41393 this.loaded = true;
41397 closeClick : function(e){
41400 this.fireEvent("beforeclose", this, o);
41401 if(o.cancel !== true){
41402 this.tabPanel.removeTab(this.id);
41406 * The text displayed in the tooltip for the close icon.
41409 closeText : "Close this tab"
41412 * This script refer to:
41413 * Title: International Telephone Input
41414 * Author: Jack O'Connor
41415 * Code version: v12.1.12
41416 * Availability: https://github.com/jackocnr/intl-tel-input.git
41419 Roo.bootstrap.PhoneInputData = function() {
41422 "Afghanistan (افغانستان)",
41427 "Albania (Shqipëri)",
41432 "Algeria (الجزائر)",
41457 "Antigua and Barbuda",
41467 "Armenia (Հայաստան)",
41483 "Austria (Österreich)",
41488 "Azerbaijan (Azərbaycan)",
41498 "Bahrain (البحرين)",
41503 "Bangladesh (বাংলাদেশ)",
41513 "Belarus (Беларусь)",
41518 "Belgium (België)",
41548 "Bosnia and Herzegovina (Босна и Херцеговина)",
41563 "British Indian Ocean Territory",
41568 "British Virgin Islands",
41578 "Bulgaria (България)",
41588 "Burundi (Uburundi)",
41593 "Cambodia (កម្ពុជា)",
41598 "Cameroon (Cameroun)",
41607 ["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"]
41610 "Cape Verde (Kabu Verdi)",
41615 "Caribbean Netherlands",
41626 "Central African Republic (République centrafricaine)",
41646 "Christmas Island",
41652 "Cocos (Keeling) Islands",
41663 "Comoros (جزر القمر)",
41668 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41673 "Congo (Republic) (Congo-Brazzaville)",
41693 "Croatia (Hrvatska)",
41714 "Czech Republic (Česká republika)",
41719 "Denmark (Danmark)",
41734 "Dominican Republic (República Dominicana)",
41738 ["809", "829", "849"]
41756 "Equatorial Guinea (Guinea Ecuatorial)",
41776 "Falkland Islands (Islas Malvinas)",
41781 "Faroe Islands (Føroyar)",
41802 "French Guiana (Guyane française)",
41807 "French Polynesia (Polynésie française)",
41822 "Georgia (საქართველო)",
41827 "Germany (Deutschland)",
41847 "Greenland (Kalaallit Nunaat)",
41884 "Guinea-Bissau (Guiné Bissau)",
41909 "Hungary (Magyarország)",
41914 "Iceland (Ísland)",
41934 "Iraq (العراق)",
41950 "Israel (ישראל)",
41977 "Jordan (الأردن)",
41982 "Kazakhstan (Казахстан)",
42003 "Kuwait (الكويت)",
42008 "Kyrgyzstan (Кыргызстан)",
42018 "Latvia (Latvija)",
42023 "Lebanon (لبنان)",
42038 "Libya (ليبيا)",
42048 "Lithuania (Lietuva)",
42063 "Macedonia (FYROM) (Македонија)",
42068 "Madagascar (Madagasikara)",
42098 "Marshall Islands",
42108 "Mauritania (موريتانيا)",
42113 "Mauritius (Moris)",
42134 "Moldova (Republica Moldova)",
42144 "Mongolia (Монгол)",
42149 "Montenegro (Crna Gora)",
42159 "Morocco (المغرب)",
42165 "Mozambique (Moçambique)",
42170 "Myanmar (Burma) (မြန်မာ)",
42175 "Namibia (Namibië)",
42190 "Netherlands (Nederland)",
42195 "New Caledonia (Nouvelle-Calédonie)",
42230 "North Korea (조선 민주주의 인민 공화국)",
42235 "Northern Mariana Islands",
42251 "Pakistan (پاکستان)",
42261 "Palestine (فلسطين)",
42271 "Papua New Guinea",
42313 "Réunion (La Réunion)",
42319 "Romania (România)",
42335 "Saint Barthélemy",
42346 "Saint Kitts and Nevis",
42356 "Saint Martin (Saint-Martin (partie française))",
42362 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42367 "Saint Vincent and the Grenadines",
42382 "São Tomé and Príncipe (São Tomé e Príncipe)",
42387 "Saudi Arabia (المملكة العربية السعودية)",
42392 "Senegal (Sénégal)",
42422 "Slovakia (Slovensko)",
42427 "Slovenia (Slovenija)",
42437 "Somalia (Soomaaliya)",
42447 "South Korea (대한민국)",
42452 "South Sudan (جنوب السودان)",
42462 "Sri Lanka (ශ්රී ලංකාව)",
42467 "Sudan (السودان)",
42477 "Svalbard and Jan Mayen",
42488 "Sweden (Sverige)",
42493 "Switzerland (Schweiz)",
42498 "Syria (سوريا)",
42543 "Trinidad and Tobago",
42548 "Tunisia (تونس)",
42553 "Turkey (Türkiye)",
42563 "Turks and Caicos Islands",
42573 "U.S. Virgin Islands",
42583 "Ukraine (Україна)",
42588 "United Arab Emirates (الإمارات العربية المتحدة)",
42610 "Uzbekistan (Oʻzbekiston)",
42620 "Vatican City (Città del Vaticano)",
42631 "Vietnam (Việt Nam)",
42636 "Wallis and Futuna (Wallis-et-Futuna)",
42641 "Western Sahara (الصحراء الغربية)",
42647 "Yemen (اليمن)",
42671 * This script refer to:
42672 * Title: International Telephone Input
42673 * Author: Jack O'Connor
42674 * Code version: v12.1.12
42675 * Availability: https://github.com/jackocnr/intl-tel-input.git
42679 * @class Roo.bootstrap.PhoneInput
42680 * @extends Roo.bootstrap.TriggerField
42681 * An input with International dial-code selection
42683 * @cfg {String} defaultDialCode default '+852'
42684 * @cfg {Array} preferedCountries default []
42687 * Create a new PhoneInput.
42688 * @param {Object} config Configuration options
42691 Roo.bootstrap.PhoneInput = function(config) {
42692 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42695 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42697 listWidth: undefined,
42699 selectedClass: 'active',
42701 invalidClass : "has-warning",
42703 validClass: 'has-success',
42705 allowed: '0123456789',
42710 * @cfg {String} defaultDialCode The default dial code when initializing the input
42712 defaultDialCode: '+852',
42715 * @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
42717 preferedCountries: false,
42719 getAutoCreate : function()
42721 var data = Roo.bootstrap.PhoneInputData();
42722 var align = this.labelAlign || this.parentLabelAlign();
42725 this.allCountries = [];
42726 this.dialCodeMapping = [];
42728 for (var i = 0; i < data.length; i++) {
42730 this.allCountries[i] = {
42734 priority: c[3] || 0,
42735 areaCodes: c[4] || null
42737 this.dialCodeMapping[c[2]] = {
42740 priority: c[3] || 0,
42741 areaCodes: c[4] || null
42753 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42754 maxlength: this.max_length,
42755 cls : 'form-control tel-input',
42756 autocomplete: 'new-password'
42759 var hiddenInput = {
42762 cls: 'hidden-tel-input'
42766 hiddenInput.name = this.name;
42769 if (this.disabled) {
42770 input.disabled = true;
42773 var flag_container = {
42790 cls: this.hasFeedback ? 'has-feedback' : '',
42796 cls: 'dial-code-holder',
42803 cls: 'roo-select2-container input-group',
42810 if (this.fieldLabel.length) {
42813 tooltip: 'This field is required'
42819 cls: 'control-label',
42825 html: this.fieldLabel
42828 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42834 if(this.indicatorpos == 'right') {
42835 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42842 if(align == 'left') {
42850 if(this.labelWidth > 12){
42851 label.style = "width: " + this.labelWidth + 'px';
42853 if(this.labelWidth < 13 && this.labelmd == 0){
42854 this.labelmd = this.labelWidth;
42856 if(this.labellg > 0){
42857 label.cls += ' col-lg-' + this.labellg;
42858 input.cls += ' col-lg-' + (12 - this.labellg);
42860 if(this.labelmd > 0){
42861 label.cls += ' col-md-' + this.labelmd;
42862 container.cls += ' col-md-' + (12 - this.labelmd);
42864 if(this.labelsm > 0){
42865 label.cls += ' col-sm-' + this.labelsm;
42866 container.cls += ' col-sm-' + (12 - this.labelsm);
42868 if(this.labelxs > 0){
42869 label.cls += ' col-xs-' + this.labelxs;
42870 container.cls += ' col-xs-' + (12 - this.labelxs);
42880 var settings = this;
42882 ['xs','sm','md','lg'].map(function(size){
42883 if (settings[size]) {
42884 cfg.cls += ' col-' + size + '-' + settings[size];
42888 this.store = new Roo.data.Store({
42889 proxy : new Roo.data.MemoryProxy({}),
42890 reader : new Roo.data.JsonReader({
42901 'name' : 'dialCode',
42905 'name' : 'priority',
42909 'name' : 'areaCodes',
42916 if(!this.preferedCountries) {
42917 this.preferedCountries = [
42924 var p = this.preferedCountries.reverse();
42927 for (var i = 0; i < p.length; i++) {
42928 for (var j = 0; j < this.allCountries.length; j++) {
42929 if(this.allCountries[j].iso2 == p[i]) {
42930 var t = this.allCountries[j];
42931 this.allCountries.splice(j,1);
42932 this.allCountries.unshift(t);
42938 this.store.proxy.data = {
42940 data: this.allCountries
42946 initEvents : function()
42949 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42951 this.indicator = this.indicatorEl();
42952 this.flag = this.flagEl();
42953 this.dialCodeHolder = this.dialCodeHolderEl();
42955 this.trigger = this.el.select('div.flag-box',true).first();
42956 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42961 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42962 _this.list.setWidth(lw);
42965 this.list.on('mouseover', this.onViewOver, this);
42966 this.list.on('mousemove', this.onViewMove, this);
42967 this.inputEl().on("keyup", this.onKeyUp, this);
42968 this.inputEl().on("keypress", this.onKeyPress, this);
42970 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42972 this.view = new Roo.View(this.list, this.tpl, {
42973 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42976 this.view.on('click', this.onViewClick, this);
42977 this.setValue(this.defaultDialCode);
42980 onTriggerClick : function(e)
42982 Roo.log('trigger click');
42987 if(this.isExpanded()){
42989 this.hasFocus = false;
42991 this.store.load({});
42992 this.hasFocus = true;
42997 isExpanded : function()
42999 return this.list.isVisible();
43002 collapse : function()
43004 if(!this.isExpanded()){
43008 Roo.get(document).un('mousedown', this.collapseIf, this);
43009 Roo.get(document).un('mousewheel', this.collapseIf, this);
43010 this.fireEvent('collapse', this);
43014 expand : function()
43018 if(this.isExpanded() || !this.hasFocus){
43022 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43023 this.list.setWidth(lw);
43026 this.restrictHeight();
43028 Roo.get(document).on('mousedown', this.collapseIf, this);
43029 Roo.get(document).on('mousewheel', this.collapseIf, this);
43031 this.fireEvent('expand', this);
43034 restrictHeight : function()
43036 this.list.alignTo(this.inputEl(), this.listAlign);
43037 this.list.alignTo(this.inputEl(), this.listAlign);
43040 onViewOver : function(e, t)
43042 if(this.inKeyMode){
43045 var item = this.view.findItemFromChild(t);
43048 var index = this.view.indexOf(item);
43049 this.select(index, false);
43054 onViewClick : function(view, doFocus, el, e)
43056 var index = this.view.getSelectedIndexes()[0];
43058 var r = this.store.getAt(index);
43061 this.onSelect(r, index);
43063 if(doFocus !== false && !this.blockFocus){
43064 this.inputEl().focus();
43068 onViewMove : function(e, t)
43070 this.inKeyMode = false;
43073 select : function(index, scrollIntoView)
43075 this.selectedIndex = index;
43076 this.view.select(index);
43077 if(scrollIntoView !== false){
43078 var el = this.view.getNode(index);
43080 this.list.scrollChildIntoView(el, false);
43085 createList : function()
43087 this.list = Roo.get(document.body).createChild({
43089 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43090 style: 'display:none'
43093 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43096 collapseIf : function(e)
43098 var in_combo = e.within(this.el);
43099 var in_list = e.within(this.list);
43100 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43102 if (in_combo || in_list || is_list) {
43108 onSelect : function(record, index)
43110 if(this.fireEvent('beforeselect', this, record, index) !== false){
43112 this.setFlagClass(record.data.iso2);
43113 this.setDialCode(record.data.dialCode);
43114 this.hasFocus = false;
43116 this.fireEvent('select', this, record, index);
43120 flagEl : function()
43122 var flag = this.el.select('div.flag',true).first();
43129 dialCodeHolderEl : function()
43131 var d = this.el.select('input.dial-code-holder',true).first();
43138 setDialCode : function(v)
43140 this.dialCodeHolder.dom.value = '+'+v;
43143 setFlagClass : function(n)
43145 this.flag.dom.className = 'flag '+n;
43148 getValue : function()
43150 var v = this.inputEl().getValue();
43151 if(this.dialCodeHolder) {
43152 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43157 setValue : function(v)
43159 var d = this.getDialCode(v);
43161 //invalid dial code
43162 if(v.length == 0 || !d || d.length == 0) {
43164 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43165 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43171 this.setFlagClass(this.dialCodeMapping[d].iso2);
43172 this.setDialCode(d);
43173 this.inputEl().dom.value = v.replace('+'+d,'');
43174 this.hiddenEl().dom.value = this.getValue();
43179 getDialCode : function(v)
43183 if (v.length == 0) {
43184 return this.dialCodeHolder.dom.value;
43188 if (v.charAt(0) != "+") {
43191 var numericChars = "";
43192 for (var i = 1; i < v.length; i++) {
43193 var c = v.charAt(i);
43196 if (this.dialCodeMapping[numericChars]) {
43197 dialCode = v.substr(1, i);
43199 if (numericChars.length == 4) {
43209 this.setValue(this.defaultDialCode);
43213 hiddenEl : function()
43215 return this.el.select('input.hidden-tel-input',true).first();
43218 // after setting val
43219 onKeyUp : function(e){
43220 this.setValue(this.getValue());
43223 onKeyPress : function(e){
43224 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43231 * @class Roo.bootstrap.MoneyField
43232 * @extends Roo.bootstrap.ComboBox
43233 * Bootstrap MoneyField class
43236 * Create a new MoneyField.
43237 * @param {Object} config Configuration options
43240 Roo.bootstrap.MoneyField = function(config) {
43242 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43246 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43249 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43251 allowDecimals : true,
43253 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43255 decimalSeparator : ".",
43257 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43259 decimalPrecision : 0,
43261 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43263 allowNegative : true,
43265 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43269 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43271 minValue : Number.NEGATIVE_INFINITY,
43273 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43275 maxValue : Number.MAX_VALUE,
43277 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43279 minText : "The minimum value for this field is {0}",
43281 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43283 maxText : "The maximum value for this field is {0}",
43285 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43286 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43288 nanText : "{0} is not a valid number",
43290 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43294 * @cfg {String} defaults currency of the MoneyField
43295 * value should be in lkey
43297 defaultCurrency : false,
43299 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43301 thousandsDelimiter : false,
43303 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43314 getAutoCreate : function()
43316 var align = this.labelAlign || this.parentLabelAlign();
43328 cls : 'form-control roo-money-amount-input',
43329 autocomplete: 'new-password'
43332 var hiddenInput = {
43336 cls: 'hidden-number-input'
43339 if(this.max_length) {
43340 input.maxlength = this.max_length;
43344 hiddenInput.name = this.name;
43347 if (this.disabled) {
43348 input.disabled = true;
43351 var clg = 12 - this.inputlg;
43352 var cmd = 12 - this.inputmd;
43353 var csm = 12 - this.inputsm;
43354 var cxs = 12 - this.inputxs;
43358 cls : 'row roo-money-field',
43362 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43366 cls: 'roo-select2-container input-group',
43370 cls : 'form-control roo-money-currency-input',
43371 autocomplete: 'new-password',
43373 name : this.currencyName
43377 cls : 'input-group-addon',
43391 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43395 cls: this.hasFeedback ? 'has-feedback' : '',
43406 if (this.fieldLabel.length) {
43409 tooltip: 'This field is required'
43415 cls: 'control-label',
43421 html: this.fieldLabel
43424 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43430 if(this.indicatorpos == 'right') {
43431 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43438 if(align == 'left') {
43446 if(this.labelWidth > 12){
43447 label.style = "width: " + this.labelWidth + 'px';
43449 if(this.labelWidth < 13 && this.labelmd == 0){
43450 this.labelmd = this.labelWidth;
43452 if(this.labellg > 0){
43453 label.cls += ' col-lg-' + this.labellg;
43454 input.cls += ' col-lg-' + (12 - this.labellg);
43456 if(this.labelmd > 0){
43457 label.cls += ' col-md-' + this.labelmd;
43458 container.cls += ' col-md-' + (12 - this.labelmd);
43460 if(this.labelsm > 0){
43461 label.cls += ' col-sm-' + this.labelsm;
43462 container.cls += ' col-sm-' + (12 - this.labelsm);
43464 if(this.labelxs > 0){
43465 label.cls += ' col-xs-' + this.labelxs;
43466 container.cls += ' col-xs-' + (12 - this.labelxs);
43477 var settings = this;
43479 ['xs','sm','md','lg'].map(function(size){
43480 if (settings[size]) {
43481 cfg.cls += ' col-' + size + '-' + settings[size];
43488 initEvents : function()
43490 this.indicator = this.indicatorEl();
43492 this.initCurrencyEvent();
43494 this.initNumberEvent();
43497 initCurrencyEvent : function()
43500 throw "can not find store for combo";
43503 this.store = Roo.factory(this.store, Roo.data);
43504 this.store.parent = this;
43508 this.triggerEl = this.el.select('.input-group-addon', true).first();
43510 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43515 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43516 _this.list.setWidth(lw);
43519 this.list.on('mouseover', this.onViewOver, this);
43520 this.list.on('mousemove', this.onViewMove, this);
43521 this.list.on('scroll', this.onViewScroll, this);
43524 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43527 this.view = new Roo.View(this.list, this.tpl, {
43528 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43531 this.view.on('click', this.onViewClick, this);
43533 this.store.on('beforeload', this.onBeforeLoad, this);
43534 this.store.on('load', this.onLoad, this);
43535 this.store.on('loadexception', this.onLoadException, this);
43537 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43538 "up" : function(e){
43539 this.inKeyMode = true;
43543 "down" : function(e){
43544 if(!this.isExpanded()){
43545 this.onTriggerClick();
43547 this.inKeyMode = true;
43552 "enter" : function(e){
43555 if(this.fireEvent("specialkey", this, e)){
43556 this.onViewClick(false);
43562 "esc" : function(e){
43566 "tab" : function(e){
43569 if(this.fireEvent("specialkey", this, e)){
43570 this.onViewClick(false);
43578 doRelay : function(foo, bar, hname){
43579 if(hname == 'down' || this.scope.isExpanded()){
43580 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43588 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43592 initNumberEvent : function(e)
43594 this.inputEl().on("keydown" , this.fireKey, this);
43595 this.inputEl().on("focus", this.onFocus, this);
43596 this.inputEl().on("blur", this.onBlur, this);
43598 this.inputEl().relayEvent('keyup', this);
43600 if(this.indicator){
43601 this.indicator.addClass('invisible');
43604 this.originalValue = this.getValue();
43606 if(this.validationEvent == 'keyup'){
43607 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43608 this.inputEl().on('keyup', this.filterValidation, this);
43610 else if(this.validationEvent !== false){
43611 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43614 if(this.selectOnFocus){
43615 this.on("focus", this.preFocus, this);
43618 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43619 this.inputEl().on("keypress", this.filterKeys, this);
43621 this.inputEl().relayEvent('keypress', this);
43624 var allowed = "0123456789";
43626 if(this.allowDecimals){
43627 allowed += this.decimalSeparator;
43630 if(this.allowNegative){
43634 if(this.thousandsDelimiter) {
43638 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43640 var keyPress = function(e){
43642 var k = e.getKey();
43644 var c = e.getCharCode();
43647 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43648 allowed.indexOf(String.fromCharCode(c)) === -1
43654 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43658 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43663 this.inputEl().on("keypress", keyPress, this);
43667 onTriggerClick : function(e)
43674 this.loadNext = false;
43676 if(this.isExpanded()){
43681 this.hasFocus = true;
43683 if(this.triggerAction == 'all') {
43684 this.doQuery(this.allQuery, true);
43688 this.doQuery(this.getRawValue());
43691 getCurrency : function()
43693 var v = this.currencyEl().getValue();
43698 restrictHeight : function()
43700 this.list.alignTo(this.currencyEl(), this.listAlign);
43701 this.list.alignTo(this.currencyEl(), this.listAlign);
43704 onViewClick : function(view, doFocus, el, e)
43706 var index = this.view.getSelectedIndexes()[0];
43708 var r = this.store.getAt(index);
43711 this.onSelect(r, index);
43715 onSelect : function(record, index){
43717 if(this.fireEvent('beforeselect', this, record, index) !== false){
43719 this.setFromCurrencyData(index > -1 ? record.data : false);
43723 this.fireEvent('select', this, record, index);
43727 setFromCurrencyData : function(o)
43731 this.lastCurrency = o;
43733 if (this.currencyField) {
43734 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43736 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43739 this.lastSelectionText = currency;
43741 //setting default currency
43742 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43743 this.setCurrency(this.defaultCurrency);
43747 this.setCurrency(currency);
43750 setFromData : function(o)
43754 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43756 this.setFromCurrencyData(c);
43761 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43763 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43766 this.setValue(value);
43770 setCurrency : function(v)
43772 this.currencyValue = v;
43775 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43780 setValue : function(v)
43782 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43788 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43790 this.inputEl().dom.value = (v == '') ? '' :
43791 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43793 if(!this.allowZero && v === '0') {
43794 this.hiddenEl().dom.value = '';
43795 this.inputEl().dom.value = '';
43802 getRawValue : function()
43804 var v = this.inputEl().getValue();
43809 getValue : function()
43811 return this.fixPrecision(this.parseValue(this.getRawValue()));
43814 parseValue : function(value)
43816 if(this.thousandsDelimiter) {
43818 r = new RegExp(",", "g");
43819 value = value.replace(r, "");
43822 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43823 return isNaN(value) ? '' : value;
43827 fixPrecision : function(value)
43829 if(this.thousandsDelimiter) {
43831 r = new RegExp(",", "g");
43832 value = value.replace(r, "");
43835 var nan = isNaN(value);
43837 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43838 return nan ? '' : value;
43840 return parseFloat(value).toFixed(this.decimalPrecision);
43843 decimalPrecisionFcn : function(v)
43845 return Math.floor(v);
43848 validateValue : function(value)
43850 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43854 var num = this.parseValue(value);
43857 this.markInvalid(String.format(this.nanText, value));
43861 if(num < this.minValue){
43862 this.markInvalid(String.format(this.minText, this.minValue));
43866 if(num > this.maxValue){
43867 this.markInvalid(String.format(this.maxText, this.maxValue));
43874 validate : function()
43876 if(this.disabled || this.allowBlank){
43881 var currency = this.getCurrency();
43883 if(this.validateValue(this.getRawValue()) && currency.length){
43888 this.markInvalid();
43892 getName: function()
43897 beforeBlur : function()
43903 var v = this.parseValue(this.getRawValue());
43910 onBlur : function()
43914 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43915 //this.el.removeClass(this.focusClass);
43918 this.hasFocus = false;
43920 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43924 var v = this.getValue();
43926 if(String(v) !== String(this.startValue)){
43927 this.fireEvent('change', this, v, this.startValue);
43930 this.fireEvent("blur", this);
43933 inputEl : function()
43935 return this.el.select('.roo-money-amount-input', true).first();
43938 currencyEl : function()
43940 return this.el.select('.roo-money-currency-input', true).first();
43943 hiddenEl : function()
43945 return this.el.select('input.hidden-number-input',true).first();
43949 * @class Roo.bootstrap.BezierSignature
43950 * @extends Roo.bootstrap.Component
43951 * Bootstrap BezierSignature class
43952 * This script refer to:
43953 * Title: Signature Pad
43955 * Availability: https://github.com/szimek/signature_pad
43958 * Create a new BezierSignature
43959 * @param {Object} config The config object
43962 Roo.bootstrap.BezierSignature = function(config){
43963 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43969 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43976 mouse_btn_down: true,
43979 * @cfg {int} canvas height
43981 canvas_height: '200px',
43984 * @cfg {float|function} Radius of a single dot.
43989 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43994 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43999 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44004 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44009 * @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.
44011 bg_color: 'rgba(0, 0, 0, 0)',
44014 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44016 dot_color: 'black',
44019 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44021 velocity_filter_weight: 0.7,
44024 * @cfg {function} Callback when stroke begin.
44029 * @cfg {function} Callback when stroke end.
44033 getAutoCreate : function()
44035 var cls = 'roo-signature column';
44038 cls += ' ' + this.cls;
44048 for(var i = 0; i < col_sizes.length; i++) {
44049 if(this[col_sizes[i]]) {
44050 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44060 cls: 'roo-signature-body',
44064 cls: 'roo-signature-body-canvas',
44065 height: this.canvas_height,
44066 width: this.canvas_width
44073 style: 'display: none'
44081 initEvents: function()
44083 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44085 var canvas = this.canvasEl();
44087 // mouse && touch event swapping...
44088 canvas.dom.style.touchAction = 'none';
44089 canvas.dom.style.msTouchAction = 'none';
44091 this.mouse_btn_down = false;
44092 canvas.on('mousedown', this._handleMouseDown, this);
44093 canvas.on('mousemove', this._handleMouseMove, this);
44094 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44096 if (window.PointerEvent) {
44097 canvas.on('pointerdown', this._handleMouseDown, this);
44098 canvas.on('pointermove', this._handleMouseMove, this);
44099 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44102 if ('ontouchstart' in window) {
44103 canvas.on('touchstart', this._handleTouchStart, this);
44104 canvas.on('touchmove', this._handleTouchMove, this);
44105 canvas.on('touchend', this._handleTouchEnd, this);
44108 Roo.EventManager.onWindowResize(this.resize, this, true);
44110 // file input event
44111 this.fileEl().on('change', this.uploadImage, this);
44118 resize: function(){
44120 var canvas = this.canvasEl().dom;
44121 var ctx = this.canvasElCtx();
44122 var img_data = false;
44124 if(canvas.width > 0) {
44125 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44127 // setting canvas width will clean img data
44130 var style = window.getComputedStyle ?
44131 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44133 var padding_left = parseInt(style.paddingLeft) || 0;
44134 var padding_right = parseInt(style.paddingRight) || 0;
44136 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44139 ctx.putImageData(img_data, 0, 0);
44143 _handleMouseDown: function(e)
44145 if (e.browserEvent.which === 1) {
44146 this.mouse_btn_down = true;
44147 this.strokeBegin(e);
44151 _handleMouseMove: function (e)
44153 if (this.mouse_btn_down) {
44154 this.strokeMoveUpdate(e);
44158 _handleMouseUp: function (e)
44160 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44161 this.mouse_btn_down = false;
44166 _handleTouchStart: function (e) {
44168 e.preventDefault();
44169 if (e.browserEvent.targetTouches.length === 1) {
44170 // var touch = e.browserEvent.changedTouches[0];
44171 // this.strokeBegin(touch);
44173 this.strokeBegin(e); // assume e catching the correct xy...
44177 _handleTouchMove: function (e) {
44178 e.preventDefault();
44179 // var touch = event.targetTouches[0];
44180 // _this._strokeMoveUpdate(touch);
44181 this.strokeMoveUpdate(e);
44184 _handleTouchEnd: function (e) {
44185 var wasCanvasTouched = e.target === this.canvasEl().dom;
44186 if (wasCanvasTouched) {
44187 e.preventDefault();
44188 // var touch = event.changedTouches[0];
44189 // _this._strokeEnd(touch);
44194 reset: function () {
44195 this._lastPoints = [];
44196 this._lastVelocity = 0;
44197 this._lastWidth = (this.min_width + this.max_width) / 2;
44198 this.canvasElCtx().fillStyle = this.dot_color;
44201 strokeMoveUpdate: function(e)
44203 this.strokeUpdate(e);
44205 if (this.throttle) {
44206 this.throttleStroke(this.strokeUpdate, this.throttle);
44209 this.strokeUpdate(e);
44213 strokeBegin: function(e)
44215 var newPointGroup = {
44216 color: this.dot_color,
44220 if (typeof this.onBegin === 'function') {
44224 this.curve_data.push(newPointGroup);
44226 this.strokeUpdate(e);
44229 strokeUpdate: function(e)
44231 var rect = this.canvasEl().dom.getBoundingClientRect();
44232 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44233 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44234 var lastPoints = lastPointGroup.points;
44235 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44236 var isLastPointTooClose = lastPoint
44237 ? point.distanceTo(lastPoint) <= this.min_distance
44239 var color = lastPointGroup.color;
44240 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44241 var curve = this.addPoint(point);
44243 this.drawDot({color: color, point: point});
44246 this.drawCurve({color: color, curve: curve});
44256 strokeEnd: function(e)
44258 this.strokeUpdate(e);
44259 if (typeof this.onEnd === 'function') {
44264 addPoint: function (point) {
44265 var _lastPoints = this._lastPoints;
44266 _lastPoints.push(point);
44267 if (_lastPoints.length > 2) {
44268 if (_lastPoints.length === 3) {
44269 _lastPoints.unshift(_lastPoints[0]);
44271 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44272 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44273 _lastPoints.shift();
44279 calculateCurveWidths: function (startPoint, endPoint) {
44280 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44281 (1 - this.velocity_filter_weight) * this._lastVelocity;
44283 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44286 start: this._lastWidth
44289 this._lastVelocity = velocity;
44290 this._lastWidth = newWidth;
44294 drawDot: function (_a) {
44295 var color = _a.color, point = _a.point;
44296 var ctx = this.canvasElCtx();
44297 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44299 this.drawCurveSegment(point.x, point.y, width);
44301 ctx.fillStyle = color;
44305 drawCurve: function (_a) {
44306 var color = _a.color, curve = _a.curve;
44307 var ctx = this.canvasElCtx();
44308 var widthDelta = curve.endWidth - curve.startWidth;
44309 var drawSteps = Math.floor(curve.length()) * 2;
44311 ctx.fillStyle = color;
44312 for (var i = 0; i < drawSteps; i += 1) {
44313 var t = i / drawSteps;
44319 var x = uuu * curve.startPoint.x;
44320 x += 3 * uu * t * curve.control1.x;
44321 x += 3 * u * tt * curve.control2.x;
44322 x += ttt * curve.endPoint.x;
44323 var y = uuu * curve.startPoint.y;
44324 y += 3 * uu * t * curve.control1.y;
44325 y += 3 * u * tt * curve.control2.y;
44326 y += ttt * curve.endPoint.y;
44327 var width = curve.startWidth + ttt * widthDelta;
44328 this.drawCurveSegment(x, y, width);
44334 drawCurveSegment: function (x, y, width) {
44335 var ctx = this.canvasElCtx();
44337 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44338 this.is_empty = false;
44343 var ctx = this.canvasElCtx();
44344 var canvas = this.canvasEl().dom;
44345 ctx.fillStyle = this.bg_color;
44346 ctx.clearRect(0, 0, canvas.width, canvas.height);
44347 ctx.fillRect(0, 0, canvas.width, canvas.height);
44348 this.curve_data = [];
44350 this.is_empty = true;
44355 return this.el.select('input',true).first();
44358 canvasEl: function()
44360 return this.el.select('canvas',true).first();
44363 canvasElCtx: function()
44365 return this.el.select('canvas',true).first().dom.getContext('2d');
44368 getImage: function(type)
44370 if(this.is_empty) {
44375 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44378 drawFromImage: function(img_src)
44380 var img = new Image();
44382 img.onload = function(){
44383 this.canvasElCtx().drawImage(img, 0, 0);
44388 this.is_empty = false;
44391 selectImage: function()
44393 this.fileEl().dom.click();
44396 uploadImage: function(e)
44398 var reader = new FileReader();
44400 reader.onload = function(e){
44401 var img = new Image();
44402 img.onload = function(){
44404 this.canvasElCtx().drawImage(img, 0, 0);
44406 img.src = e.target.result;
44409 reader.readAsDataURL(e.target.files[0]);
44412 // Bezier Point Constructor
44413 Point: (function () {
44414 function Point(x, y, time) {
44417 this.time = time || Date.now();
44419 Point.prototype.distanceTo = function (start) {
44420 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44422 Point.prototype.equals = function (other) {
44423 return this.x === other.x && this.y === other.y && this.time === other.time;
44425 Point.prototype.velocityFrom = function (start) {
44426 return this.time !== start.time
44427 ? this.distanceTo(start) / (this.time - start.time)
44434 // Bezier Constructor
44435 Bezier: (function () {
44436 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44437 this.startPoint = startPoint;
44438 this.control2 = control2;
44439 this.control1 = control1;
44440 this.endPoint = endPoint;
44441 this.startWidth = startWidth;
44442 this.endWidth = endWidth;
44444 Bezier.fromPoints = function (points, widths, scope) {
44445 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44446 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44447 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44449 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44450 var dx1 = s1.x - s2.x;
44451 var dy1 = s1.y - s2.y;
44452 var dx2 = s2.x - s3.x;
44453 var dy2 = s2.y - s3.y;
44454 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44455 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44456 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44457 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44458 var dxm = m1.x - m2.x;
44459 var dym = m1.y - m2.y;
44460 var k = l2 / (l1 + l2);
44461 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44462 var tx = s2.x - cm.x;
44463 var ty = s2.y - cm.y;
44465 c1: new scope.Point(m1.x + tx, m1.y + ty),
44466 c2: new scope.Point(m2.x + tx, m2.y + ty)
44469 Bezier.prototype.length = function () {
44474 for (var i = 0; i <= steps; i += 1) {
44476 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44477 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44479 var xdiff = cx - px;
44480 var ydiff = cy - py;
44481 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44488 Bezier.prototype.point = function (t, start, c1, c2, end) {
44489 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44490 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44491 + (3.0 * c2 * (1.0 - t) * t * t)
44492 + (end * t * t * t);
44497 throttleStroke: function(fn, wait) {
44498 if (wait === void 0) { wait = 250; }
44500 var timeout = null;
44504 var later = function () {
44505 previous = Date.now();
44507 result = fn.apply(storedContext, storedArgs);
44509 storedContext = null;
44513 return function wrapper() {
44515 for (var _i = 0; _i < arguments.length; _i++) {
44516 args[_i] = arguments[_i];
44518 var now = Date.now();
44519 var remaining = wait - (now - previous);
44520 storedContext = this;
44522 if (remaining <= 0 || remaining > wait) {
44524 clearTimeout(timeout);
44528 result = fn.apply(storedContext, storedArgs);
44530 storedContext = null;
44534 else if (!timeout) {
44535 timeout = window.setTimeout(later, remaining);