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
12526 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12529 errorTimeout : 3000,
12533 fileCollection : false,
12536 getAutoCreate : function()
12540 cls :'form-group' ,
12545 //cls : 'input-group-addon',
12546 html : this.fieldLabel
12554 value : this.value,
12555 cls : 'd-none form-control'
12560 multiple : 'multiple',
12562 cls : 'd-none roo-card-upload-selector'
12566 cls : 'roo-card-uploader-button-container w-100 mb-2'
12569 cls : 'card-columns roo-card-uploader-container'
12579 getChildContainer : function() /// what children are added to.
12581 return this.containerEl;
12584 getButtonContainer : function() /// what children are added to.
12586 return this.el.select(".roo-card-uploader-button-container").first();
12589 initEvents : function()
12592 Roo.bootstrap.Input.prototype.initEvents.call(this);
12596 xns: Roo.bootstrap,
12599 container_method : 'getButtonContainer' ,
12600 html : this.html, // fix changable?
12603 'click' : function(btn, e) {
12612 this.urlAPI = (window.createObjectURL && window) ||
12613 (window.URL && URL.revokeObjectURL && URL) ||
12614 (window.webkitURL && webkitURL);
12619 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12621 this.selectorEl.on('change', this.onFileSelected, this);
12624 this.images.forEach(function(img) {
12627 this.images = false;
12629 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12635 onClick : function(e)
12637 e.preventDefault();
12639 this.selectorEl.dom.click();
12643 onFileSelected : function(e)
12645 e.preventDefault();
12647 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12651 Roo.each(this.selectorEl.dom.files, function(file){
12652 this.addFile(file);
12661 addFile : function(file)
12664 if(typeof(file) === 'string'){
12665 throw "Add file by name?"; // should not happen
12669 if(!file || !this.urlAPI){
12679 var url = _this.urlAPI.createObjectURL( file);
12682 id : Roo.bootstrap.CardUploader.ID--,
12683 is_uploaded : false,
12687 mimetype : file.type,
12695 * addCard - add an Attachment to the uploader
12696 * @param data - the data about the image to upload
12700 title : "Title of file",
12701 is_uploaded : false,
12702 src : "http://.....",
12703 srcfile : { the File upload object },
12704 mimetype : file.type,
12707 .. any other data...
12713 addCard : function (data)
12715 // hidden input element?
12716 // if the file is not an image...
12717 //then we need to use something other that and header_image
12722 xns : Roo.bootstrap,
12723 xtype : 'CardFooter',
12726 xns : Roo.bootstrap,
12732 xns : Roo.bootstrap,
12734 html : String.format("<small>{0}</small>", data.title),
12735 cls : 'col-10 text-left',
12740 click : function() {
12742 t.fireEvent( "download", t, data );
12748 xns : Roo.bootstrap,
12750 style: 'max-height: 28px; ',
12756 click : function() {
12757 t.removeCard(data.id)
12769 var cn = this.addxtype(
12772 xns : Roo.bootstrap,
12775 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12776 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12777 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12782 initEvents : function() {
12783 Roo.bootstrap.Card.prototype.initEvents.call(this);
12785 this.imgEl = this.el.select('.card-img-top').first();
12787 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12788 this.imgEl.set({ 'pointer' : 'cursor' });
12791 this.getCardFooter().addClass('p-1');
12798 // dont' really need ot update items.
12799 // this.items.push(cn);
12800 this.fileCollection.add(cn);
12802 if (!data.srcfile) {
12803 this.updateInput();
12808 var reader = new FileReader();
12809 reader.addEventListener("load", function() {
12810 data.srcdata = reader.result;
12813 reader.readAsDataURL(data.srcfile);
12818 removeCard : function(id)
12821 var card = this.fileCollection.get(id);
12822 card.data.is_deleted = 1;
12823 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12824 //this.fileCollection.remove(card);
12825 //this.items = this.items.filter(function(e) { return e != card });
12826 // dont' really need ot update items.
12827 card.el.dom.parentNode.removeChild(card.el.dom);
12828 this.updateInput();
12834 this.fileCollection.each(function(card) {
12835 if (card.el.dom && card.el.dom.parentNode) {
12836 card.el.dom.parentNode.removeChild(card.el.dom);
12839 this.fileCollection.clear();
12840 this.updateInput();
12843 updateInput : function()
12846 this.fileCollection.each(function(e) {
12850 this.inputEl().dom.value = JSON.stringify(data);
12860 Roo.bootstrap.CardUploader.ID = -1;/*
12862 * Ext JS Library 1.1.1
12863 * Copyright(c) 2006-2007, Ext JS, LLC.
12865 * Originally Released Under LGPL - original licence link has changed is not relivant.
12868 * <script type="text/javascript">
12873 * @class Roo.data.SortTypes
12875 * Defines the default sorting (casting?) comparison functions used when sorting data.
12877 Roo.data.SortTypes = {
12879 * Default sort that does nothing
12880 * @param {Mixed} s The value being converted
12881 * @return {Mixed} The comparison value
12883 none : function(s){
12888 * The regular expression used to strip tags
12892 stripTagsRE : /<\/?[^>]+>/gi,
12895 * Strips all HTML tags to sort on text only
12896 * @param {Mixed} s The value being converted
12897 * @return {String} The comparison value
12899 asText : function(s){
12900 return String(s).replace(this.stripTagsRE, "");
12904 * Strips all HTML tags to sort on text only - Case insensitive
12905 * @param {Mixed} s The value being converted
12906 * @return {String} The comparison value
12908 asUCText : function(s){
12909 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12913 * Case insensitive string
12914 * @param {Mixed} s The value being converted
12915 * @return {String} The comparison value
12917 asUCString : function(s) {
12918 return String(s).toUpperCase();
12923 * @param {Mixed} s The value being converted
12924 * @return {Number} The comparison value
12926 asDate : function(s) {
12930 if(s instanceof Date){
12931 return s.getTime();
12933 return Date.parse(String(s));
12938 * @param {Mixed} s The value being converted
12939 * @return {Float} The comparison value
12941 asFloat : function(s) {
12942 var val = parseFloat(String(s).replace(/,/g, ""));
12951 * @param {Mixed} s The value being converted
12952 * @return {Number} The comparison value
12954 asInt : function(s) {
12955 var val = parseInt(String(s).replace(/,/g, ""));
12963 * Ext JS Library 1.1.1
12964 * Copyright(c) 2006-2007, Ext JS, LLC.
12966 * Originally Released Under LGPL - original licence link has changed is not relivant.
12969 * <script type="text/javascript">
12973 * @class Roo.data.Record
12974 * Instances of this class encapsulate both record <em>definition</em> information, and record
12975 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12976 * to access Records cached in an {@link Roo.data.Store} object.<br>
12978 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12979 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12982 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12984 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12985 * {@link #create}. The parameters are the same.
12986 * @param {Array} data An associative Array of data values keyed by the field name.
12987 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12988 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12989 * not specified an integer id is generated.
12991 Roo.data.Record = function(data, id){
12992 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12997 * Generate a constructor for a specific record layout.
12998 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12999 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13000 * Each field definition object may contain the following properties: <ul>
13001 * <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,
13002 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13003 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13004 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13005 * is being used, then this is a string containing the javascript expression to reference the data relative to
13006 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13007 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13008 * this may be omitted.</p></li>
13009 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13010 * <ul><li>auto (Default, implies no conversion)</li>
13015 * <li>date</li></ul></p></li>
13016 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13017 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13018 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13019 * by the Reader into an object that will be stored in the Record. It is passed the
13020 * following parameters:<ul>
13021 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13023 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13025 * <br>usage:<br><pre><code>
13026 var TopicRecord = Roo.data.Record.create(
13027 {name: 'title', mapping: 'topic_title'},
13028 {name: 'author', mapping: 'username'},
13029 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13030 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13031 {name: 'lastPoster', mapping: 'user2'},
13032 {name: 'excerpt', mapping: 'post_text'}
13035 var myNewRecord = new TopicRecord({
13036 title: 'Do my job please',
13039 lastPost: new Date(),
13040 lastPoster: 'Animal',
13041 excerpt: 'No way dude!'
13043 myStore.add(myNewRecord);
13048 Roo.data.Record.create = function(o){
13049 var f = function(){
13050 f.superclass.constructor.apply(this, arguments);
13052 Roo.extend(f, Roo.data.Record);
13053 var p = f.prototype;
13054 p.fields = new Roo.util.MixedCollection(false, function(field){
13057 for(var i = 0, len = o.length; i < len; i++){
13058 p.fields.add(new Roo.data.Field(o[i]));
13060 f.getField = function(name){
13061 return p.fields.get(name);
13066 Roo.data.Record.AUTO_ID = 1000;
13067 Roo.data.Record.EDIT = 'edit';
13068 Roo.data.Record.REJECT = 'reject';
13069 Roo.data.Record.COMMIT = 'commit';
13071 Roo.data.Record.prototype = {
13073 * Readonly flag - true if this record has been modified.
13082 join : function(store){
13083 this.store = store;
13087 * Set the named field to the specified value.
13088 * @param {String} name The name of the field to set.
13089 * @param {Object} value The value to set the field to.
13091 set : function(name, value){
13092 if(this.data[name] == value){
13096 if(!this.modified){
13097 this.modified = {};
13099 if(typeof this.modified[name] == 'undefined'){
13100 this.modified[name] = this.data[name];
13102 this.data[name] = value;
13103 if(!this.editing && this.store){
13104 this.store.afterEdit(this);
13109 * Get the value of the named field.
13110 * @param {String} name The name of the field to get the value of.
13111 * @return {Object} The value of the field.
13113 get : function(name){
13114 return this.data[name];
13118 beginEdit : function(){
13119 this.editing = true;
13120 this.modified = {};
13124 cancelEdit : function(){
13125 this.editing = false;
13126 delete this.modified;
13130 endEdit : function(){
13131 this.editing = false;
13132 if(this.dirty && this.store){
13133 this.store.afterEdit(this);
13138 * Usually called by the {@link Roo.data.Store} which owns the Record.
13139 * Rejects all changes made to the Record since either creation, or the last commit operation.
13140 * Modified fields are reverted to their original values.
13142 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13143 * of reject operations.
13145 reject : function(){
13146 var m = this.modified;
13148 if(typeof m[n] != "function"){
13149 this.data[n] = m[n];
13152 this.dirty = false;
13153 delete this.modified;
13154 this.editing = false;
13156 this.store.afterReject(this);
13161 * Usually called by the {@link Roo.data.Store} which owns the Record.
13162 * Commits all changes made to the Record since either creation, or the last commit operation.
13164 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13165 * of commit operations.
13167 commit : function(){
13168 this.dirty = false;
13169 delete this.modified;
13170 this.editing = false;
13172 this.store.afterCommit(this);
13177 hasError : function(){
13178 return this.error != null;
13182 clearError : function(){
13187 * Creates a copy of this record.
13188 * @param {String} id (optional) A new record id if you don't want to use this record's id
13191 copy : function(newId) {
13192 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13196 * Ext JS Library 1.1.1
13197 * Copyright(c) 2006-2007, Ext JS, LLC.
13199 * Originally Released Under LGPL - original licence link has changed is not relivant.
13202 * <script type="text/javascript">
13208 * @class Roo.data.Store
13209 * @extends Roo.util.Observable
13210 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13211 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13213 * 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
13214 * has no knowledge of the format of the data returned by the Proxy.<br>
13216 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13217 * instances from the data object. These records are cached and made available through accessor functions.
13219 * Creates a new Store.
13220 * @param {Object} config A config object containing the objects needed for the Store to access data,
13221 * and read the data into Records.
13223 Roo.data.Store = function(config){
13224 this.data = new Roo.util.MixedCollection(false);
13225 this.data.getKey = function(o){
13228 this.baseParams = {};
13230 this.paramNames = {
13235 "multisort" : "_multisort"
13238 if(config && config.data){
13239 this.inlineData = config.data;
13240 delete config.data;
13243 Roo.apply(this, config);
13245 if(this.reader){ // reader passed
13246 this.reader = Roo.factory(this.reader, Roo.data);
13247 this.reader.xmodule = this.xmodule || false;
13248 if(!this.recordType){
13249 this.recordType = this.reader.recordType;
13251 if(this.reader.onMetaChange){
13252 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13256 if(this.recordType){
13257 this.fields = this.recordType.prototype.fields;
13259 this.modified = [];
13263 * @event datachanged
13264 * Fires when the data cache has changed, and a widget which is using this Store
13265 * as a Record cache should refresh its view.
13266 * @param {Store} this
13268 datachanged : true,
13270 * @event metachange
13271 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13272 * @param {Store} this
13273 * @param {Object} meta The JSON metadata
13278 * Fires when Records have been added to the Store
13279 * @param {Store} this
13280 * @param {Roo.data.Record[]} records The array of Records added
13281 * @param {Number} index The index at which the record(s) were added
13286 * Fires when a Record has been removed from the Store
13287 * @param {Store} this
13288 * @param {Roo.data.Record} record The Record that was removed
13289 * @param {Number} index The index at which the record was removed
13294 * Fires when a Record has been updated
13295 * @param {Store} this
13296 * @param {Roo.data.Record} record The Record that was updated
13297 * @param {String} operation The update operation being performed. Value may be one of:
13299 Roo.data.Record.EDIT
13300 Roo.data.Record.REJECT
13301 Roo.data.Record.COMMIT
13307 * Fires when the data cache has been cleared.
13308 * @param {Store} this
13312 * @event beforeload
13313 * Fires before a request is made for a new data object. If the beforeload handler returns false
13314 * the load action will be canceled.
13315 * @param {Store} this
13316 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13320 * @event beforeloadadd
13321 * Fires after a new set of Records has been loaded.
13322 * @param {Store} this
13323 * @param {Roo.data.Record[]} records The Records that were loaded
13324 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13326 beforeloadadd : true,
13329 * Fires after a new set of Records has been loaded, before they are added to the store.
13330 * @param {Store} this
13331 * @param {Roo.data.Record[]} records The Records that were loaded
13332 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13333 * @params {Object} return from reader
13337 * @event loadexception
13338 * Fires if an exception occurs in the Proxy during loading.
13339 * Called with the signature of the Proxy's "loadexception" event.
13340 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13343 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13344 * @param {Object} load options
13345 * @param {Object} jsonData from your request (normally this contains the Exception)
13347 loadexception : true
13351 this.proxy = Roo.factory(this.proxy, Roo.data);
13352 this.proxy.xmodule = this.xmodule || false;
13353 this.relayEvents(this.proxy, ["loadexception"]);
13355 this.sortToggle = {};
13356 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13358 Roo.data.Store.superclass.constructor.call(this);
13360 if(this.inlineData){
13361 this.loadData(this.inlineData);
13362 delete this.inlineData;
13366 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13368 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13369 * without a remote query - used by combo/forms at present.
13373 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13376 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13379 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13380 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13383 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13384 * on any HTTP request
13387 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13390 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13394 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13395 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13397 remoteSort : false,
13400 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13401 * loaded or when a record is removed. (defaults to false).
13403 pruneModifiedRecords : false,
13406 lastOptions : null,
13409 * Add Records to the Store and fires the add event.
13410 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13412 add : function(records){
13413 records = [].concat(records);
13414 for(var i = 0, len = records.length; i < len; i++){
13415 records[i].join(this);
13417 var index = this.data.length;
13418 this.data.addAll(records);
13419 this.fireEvent("add", this, records, index);
13423 * Remove a Record from the Store and fires the remove event.
13424 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13426 remove : function(record){
13427 var index = this.data.indexOf(record);
13428 this.data.removeAt(index);
13430 if(this.pruneModifiedRecords){
13431 this.modified.remove(record);
13433 this.fireEvent("remove", this, record, index);
13437 * Remove all Records from the Store and fires the clear event.
13439 removeAll : function(){
13441 if(this.pruneModifiedRecords){
13442 this.modified = [];
13444 this.fireEvent("clear", this);
13448 * Inserts Records to the Store at the given index and fires the add event.
13449 * @param {Number} index The start index at which to insert the passed Records.
13450 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13452 insert : function(index, records){
13453 records = [].concat(records);
13454 for(var i = 0, len = records.length; i < len; i++){
13455 this.data.insert(index, records[i]);
13456 records[i].join(this);
13458 this.fireEvent("add", this, records, index);
13462 * Get the index within the cache of the passed Record.
13463 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13464 * @return {Number} The index of the passed Record. Returns -1 if not found.
13466 indexOf : function(record){
13467 return this.data.indexOf(record);
13471 * Get the index within the cache of the Record with the passed id.
13472 * @param {String} id The id of the Record to find.
13473 * @return {Number} The index of the Record. Returns -1 if not found.
13475 indexOfId : function(id){
13476 return this.data.indexOfKey(id);
13480 * Get the Record with the specified id.
13481 * @param {String} id The id of the Record to find.
13482 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13484 getById : function(id){
13485 return this.data.key(id);
13489 * Get the Record at the specified index.
13490 * @param {Number} index The index of the Record to find.
13491 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13493 getAt : function(index){
13494 return this.data.itemAt(index);
13498 * Returns a range of Records between specified indices.
13499 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13500 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13501 * @return {Roo.data.Record[]} An array of Records
13503 getRange : function(start, end){
13504 return this.data.getRange(start, end);
13508 storeOptions : function(o){
13509 o = Roo.apply({}, o);
13512 this.lastOptions = o;
13516 * Loads the Record cache from the configured Proxy using the configured Reader.
13518 * If using remote paging, then the first load call must specify the <em>start</em>
13519 * and <em>limit</em> properties in the options.params property to establish the initial
13520 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13522 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13523 * and this call will return before the new data has been loaded. Perform any post-processing
13524 * in a callback function, or in a "load" event handler.</strong>
13526 * @param {Object} options An object containing properties which control loading options:<ul>
13527 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13528 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13529 * passed the following arguments:<ul>
13530 * <li>r : Roo.data.Record[]</li>
13531 * <li>options: Options object from the load call</li>
13532 * <li>success: Boolean success indicator</li></ul></li>
13533 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13534 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13537 load : function(options){
13538 options = options || {};
13539 if(this.fireEvent("beforeload", this, options) !== false){
13540 this.storeOptions(options);
13541 var p = Roo.apply(options.params || {}, this.baseParams);
13542 // if meta was not loaded from remote source.. try requesting it.
13543 if (!this.reader.metaFromRemote) {
13544 p._requestMeta = 1;
13546 if(this.sortInfo && this.remoteSort){
13547 var pn = this.paramNames;
13548 p[pn["sort"]] = this.sortInfo.field;
13549 p[pn["dir"]] = this.sortInfo.direction;
13551 if (this.multiSort) {
13552 var pn = this.paramNames;
13553 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13556 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13561 * Reloads the Record cache from the configured Proxy using the configured Reader and
13562 * the options from the last load operation performed.
13563 * @param {Object} options (optional) An object containing properties which may override the options
13564 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13565 * the most recently used options are reused).
13567 reload : function(options){
13568 this.load(Roo.applyIf(options||{}, this.lastOptions));
13572 // Called as a callback by the Reader during a load operation.
13573 loadRecords : function(o, options, success){
13574 if(!o || success === false){
13575 if(success !== false){
13576 this.fireEvent("load", this, [], options, o);
13578 if(options.callback){
13579 options.callback.call(options.scope || this, [], options, false);
13583 // if data returned failure - throw an exception.
13584 if (o.success === false) {
13585 // show a message if no listener is registered.
13586 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13587 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13589 // loadmask wil be hooked into this..
13590 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13593 var r = o.records, t = o.totalRecords || r.length;
13595 this.fireEvent("beforeloadadd", this, r, options, o);
13597 if(!options || options.add !== true){
13598 if(this.pruneModifiedRecords){
13599 this.modified = [];
13601 for(var i = 0, len = r.length; i < len; i++){
13605 this.data = this.snapshot;
13606 delete this.snapshot;
13609 this.data.addAll(r);
13610 this.totalLength = t;
13612 this.fireEvent("datachanged", this);
13614 this.totalLength = Math.max(t, this.data.length+r.length);
13618 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13620 var e = new Roo.data.Record({});
13622 e.set(this.parent.displayField, this.parent.emptyTitle);
13623 e.set(this.parent.valueField, '');
13628 this.fireEvent("load", this, r, options, o);
13629 if(options.callback){
13630 options.callback.call(options.scope || this, r, options, true);
13636 * Loads data from a passed data block. A Reader which understands the format of the data
13637 * must have been configured in the constructor.
13638 * @param {Object} data The data block from which to read the Records. The format of the data expected
13639 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13640 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13642 loadData : function(o, append){
13643 var r = this.reader.readRecords(o);
13644 this.loadRecords(r, {add: append}, true);
13648 * using 'cn' the nested child reader read the child array into it's child stores.
13649 * @param {Object} rec The record with a 'children array
13651 loadDataFromChildren : function(rec)
13653 this.loadData(this.reader.toLoadData(rec));
13658 * Gets the number of cached records.
13660 * <em>If using paging, this may not be the total size of the dataset. If the data object
13661 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13662 * the data set size</em>
13664 getCount : function(){
13665 return this.data.length || 0;
13669 * Gets the total number of records in the dataset as returned by the server.
13671 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13672 * the dataset size</em>
13674 getTotalCount : function(){
13675 return this.totalLength || 0;
13679 * Returns the sort state of the Store as an object with two properties:
13681 field {String} The name of the field by which the Records are sorted
13682 direction {String} The sort order, "ASC" or "DESC"
13685 getSortState : function(){
13686 return this.sortInfo;
13690 applySort : function(){
13691 if(this.sortInfo && !this.remoteSort){
13692 var s = this.sortInfo, f = s.field;
13693 var st = this.fields.get(f).sortType;
13694 var fn = function(r1, r2){
13695 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13696 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13698 this.data.sort(s.direction, fn);
13699 if(this.snapshot && this.snapshot != this.data){
13700 this.snapshot.sort(s.direction, fn);
13706 * Sets the default sort column and order to be used by the next load operation.
13707 * @param {String} fieldName The name of the field to sort by.
13708 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13710 setDefaultSort : function(field, dir){
13711 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13715 * Sort the Records.
13716 * If remote sorting is used, the sort is performed on the server, and the cache is
13717 * reloaded. If local sorting is used, the cache is sorted internally.
13718 * @param {String} fieldName The name of the field to sort by.
13719 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13721 sort : function(fieldName, dir){
13722 var f = this.fields.get(fieldName);
13724 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13726 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13727 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13732 this.sortToggle[f.name] = dir;
13733 this.sortInfo = {field: f.name, direction: dir};
13734 if(!this.remoteSort){
13736 this.fireEvent("datachanged", this);
13738 this.load(this.lastOptions);
13743 * Calls the specified function for each of the Records in the cache.
13744 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13745 * Returning <em>false</em> aborts and exits the iteration.
13746 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13748 each : function(fn, scope){
13749 this.data.each(fn, scope);
13753 * Gets all records modified since the last commit. Modified records are persisted across load operations
13754 * (e.g., during paging).
13755 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13757 getModifiedRecords : function(){
13758 return this.modified;
13762 createFilterFn : function(property, value, anyMatch){
13763 if(!value.exec){ // not a regex
13764 value = String(value);
13765 if(value.length == 0){
13768 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13770 return function(r){
13771 return value.test(r.data[property]);
13776 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13777 * @param {String} property A field on your records
13778 * @param {Number} start The record index to start at (defaults to 0)
13779 * @param {Number} end The last record index to include (defaults to length - 1)
13780 * @return {Number} The sum
13782 sum : function(property, start, end){
13783 var rs = this.data.items, v = 0;
13784 start = start || 0;
13785 end = (end || end === 0) ? end : rs.length-1;
13787 for(var i = start; i <= end; i++){
13788 v += (rs[i].data[property] || 0);
13794 * Filter the records by a specified property.
13795 * @param {String} field A field on your records
13796 * @param {String/RegExp} value Either a string that the field
13797 * should start with or a RegExp to test against the field
13798 * @param {Boolean} anyMatch True to match any part not just the beginning
13800 filter : function(property, value, anyMatch){
13801 var fn = this.createFilterFn(property, value, anyMatch);
13802 return fn ? this.filterBy(fn) : this.clearFilter();
13806 * Filter by a function. The specified function will be called with each
13807 * record in this data source. If the function returns true the record is included,
13808 * otherwise it is filtered.
13809 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13810 * @param {Object} scope (optional) The scope of the function (defaults to this)
13812 filterBy : function(fn, scope){
13813 this.snapshot = this.snapshot || this.data;
13814 this.data = this.queryBy(fn, scope||this);
13815 this.fireEvent("datachanged", this);
13819 * Query the records by a specified property.
13820 * @param {String} field A field on your records
13821 * @param {String/RegExp} value Either a string that the field
13822 * should start with or a RegExp to test against the field
13823 * @param {Boolean} anyMatch True to match any part not just the beginning
13824 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13826 query : function(property, value, anyMatch){
13827 var fn = this.createFilterFn(property, value, anyMatch);
13828 return fn ? this.queryBy(fn) : this.data.clone();
13832 * Query by a function. The specified function will be called with each
13833 * record in this data source. If the function returns true the record is included
13835 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13836 * @param {Object} scope (optional) The scope of the function (defaults to this)
13837 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13839 queryBy : function(fn, scope){
13840 var data = this.snapshot || this.data;
13841 return data.filterBy(fn, scope||this);
13845 * Collects unique values for a particular dataIndex from this store.
13846 * @param {String} dataIndex The property to collect
13847 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13848 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13849 * @return {Array} An array of the unique values
13851 collect : function(dataIndex, allowNull, bypassFilter){
13852 var d = (bypassFilter === true && this.snapshot) ?
13853 this.snapshot.items : this.data.items;
13854 var v, sv, r = [], l = {};
13855 for(var i = 0, len = d.length; i < len; i++){
13856 v = d[i].data[dataIndex];
13858 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13867 * Revert to a view of the Record cache with no filtering applied.
13868 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13870 clearFilter : function(suppressEvent){
13871 if(this.snapshot && this.snapshot != this.data){
13872 this.data = this.snapshot;
13873 delete this.snapshot;
13874 if(suppressEvent !== true){
13875 this.fireEvent("datachanged", this);
13881 afterEdit : function(record){
13882 if(this.modified.indexOf(record) == -1){
13883 this.modified.push(record);
13885 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13889 afterReject : function(record){
13890 this.modified.remove(record);
13891 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13895 afterCommit : function(record){
13896 this.modified.remove(record);
13897 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13901 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13902 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13904 commitChanges : function(){
13905 var m = this.modified.slice(0);
13906 this.modified = [];
13907 for(var i = 0, len = m.length; i < len; i++){
13913 * Cancel outstanding changes on all changed records.
13915 rejectChanges : function(){
13916 var m = this.modified.slice(0);
13917 this.modified = [];
13918 for(var i = 0, len = m.length; i < len; i++){
13923 onMetaChange : function(meta, rtype, o){
13924 this.recordType = rtype;
13925 this.fields = rtype.prototype.fields;
13926 delete this.snapshot;
13927 this.sortInfo = meta.sortInfo || this.sortInfo;
13928 this.modified = [];
13929 this.fireEvent('metachange', this, this.reader.meta);
13932 moveIndex : function(data, type)
13934 var index = this.indexOf(data);
13936 var newIndex = index + type;
13940 this.insert(newIndex, data);
13945 * Ext JS Library 1.1.1
13946 * Copyright(c) 2006-2007, Ext JS, LLC.
13948 * Originally Released Under LGPL - original licence link has changed is not relivant.
13951 * <script type="text/javascript">
13955 * @class Roo.data.SimpleStore
13956 * @extends Roo.data.Store
13957 * Small helper class to make creating Stores from Array data easier.
13958 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13959 * @cfg {Array} fields An array of field definition objects, or field name strings.
13960 * @cfg {Object} an existing reader (eg. copied from another store)
13961 * @cfg {Array} data The multi-dimensional array of data
13963 * @param {Object} config
13965 Roo.data.SimpleStore = function(config)
13967 Roo.data.SimpleStore.superclass.constructor.call(this, {
13969 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13972 Roo.data.Record.create(config.fields)
13974 proxy : new Roo.data.MemoryProxy(config.data)
13978 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13980 * Ext JS Library 1.1.1
13981 * Copyright(c) 2006-2007, Ext JS, LLC.
13983 * Originally Released Under LGPL - original licence link has changed is not relivant.
13986 * <script type="text/javascript">
13991 * @extends Roo.data.Store
13992 * @class Roo.data.JsonStore
13993 * Small helper class to make creating Stores for JSON data easier. <br/>
13995 var store = new Roo.data.JsonStore({
13996 url: 'get-images.php',
13998 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14001 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14002 * JsonReader and HttpProxy (unless inline data is provided).</b>
14003 * @cfg {Array} fields An array of field definition objects, or field name strings.
14005 * @param {Object} config
14007 Roo.data.JsonStore = function(c){
14008 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14009 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14010 reader: new Roo.data.JsonReader(c, c.fields)
14013 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14015 * Ext JS Library 1.1.1
14016 * Copyright(c) 2006-2007, Ext JS, LLC.
14018 * Originally Released Under LGPL - original licence link has changed is not relivant.
14021 * <script type="text/javascript">
14025 Roo.data.Field = function(config){
14026 if(typeof config == "string"){
14027 config = {name: config};
14029 Roo.apply(this, config);
14032 this.type = "auto";
14035 var st = Roo.data.SortTypes;
14036 // named sortTypes are supported, here we look them up
14037 if(typeof this.sortType == "string"){
14038 this.sortType = st[this.sortType];
14041 // set default sortType for strings and dates
14042 if(!this.sortType){
14045 this.sortType = st.asUCString;
14048 this.sortType = st.asDate;
14051 this.sortType = st.none;
14056 var stripRe = /[\$,%]/g;
14058 // prebuilt conversion function for this field, instead of
14059 // switching every time we're reading a value
14061 var cv, dateFormat = this.dateFormat;
14066 cv = function(v){ return v; };
14069 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14073 return v !== undefined && v !== null && v !== '' ?
14074 parseInt(String(v).replace(stripRe, ""), 10) : '';
14079 return v !== undefined && v !== null && v !== '' ?
14080 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14085 cv = function(v){ return v === true || v === "true" || v == 1; };
14092 if(v instanceof Date){
14096 if(dateFormat == "timestamp"){
14097 return new Date(v*1000);
14099 return Date.parseDate(v, dateFormat);
14101 var parsed = Date.parse(v);
14102 return parsed ? new Date(parsed) : null;
14111 Roo.data.Field.prototype = {
14119 * Ext JS Library 1.1.1
14120 * Copyright(c) 2006-2007, Ext JS, LLC.
14122 * Originally Released Under LGPL - original licence link has changed is not relivant.
14125 * <script type="text/javascript">
14128 // Base class for reading structured data from a data source. This class is intended to be
14129 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14132 * @class Roo.data.DataReader
14133 * Base class for reading structured data from a data source. This class is intended to be
14134 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14137 Roo.data.DataReader = function(meta, recordType){
14141 this.recordType = recordType instanceof Array ?
14142 Roo.data.Record.create(recordType) : recordType;
14145 Roo.data.DataReader.prototype = {
14148 readerType : 'Data',
14150 * Create an empty record
14151 * @param {Object} data (optional) - overlay some values
14152 * @return {Roo.data.Record} record created.
14154 newRow : function(d) {
14156 this.recordType.prototype.fields.each(function(c) {
14158 case 'int' : da[c.name] = 0; break;
14159 case 'date' : da[c.name] = new Date(); break;
14160 case 'float' : da[c.name] = 0.0; break;
14161 case 'boolean' : da[c.name] = false; break;
14162 default : da[c.name] = ""; break;
14166 return new this.recordType(Roo.apply(da, d));
14172 * Ext JS Library 1.1.1
14173 * Copyright(c) 2006-2007, Ext JS, LLC.
14175 * Originally Released Under LGPL - original licence link has changed is not relivant.
14178 * <script type="text/javascript">
14182 * @class Roo.data.DataProxy
14183 * @extends Roo.data.Observable
14184 * This class is an abstract base class for implementations which provide retrieval of
14185 * unformatted data objects.<br>
14187 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14188 * (of the appropriate type which knows how to parse the data object) to provide a block of
14189 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14191 * Custom implementations must implement the load method as described in
14192 * {@link Roo.data.HttpProxy#load}.
14194 Roo.data.DataProxy = function(){
14197 * @event beforeload
14198 * Fires before a network request is made to retrieve a data object.
14199 * @param {Object} This DataProxy object.
14200 * @param {Object} params The params parameter to the load function.
14205 * Fires before the load method's callback is called.
14206 * @param {Object} This DataProxy object.
14207 * @param {Object} o The data object.
14208 * @param {Object} arg The callback argument object passed to the load function.
14212 * @event loadexception
14213 * Fires if an Exception occurs during data retrieval.
14214 * @param {Object} This DataProxy object.
14215 * @param {Object} o The data object.
14216 * @param {Object} arg The callback argument object passed to the load function.
14217 * @param {Object} e The Exception.
14219 loadexception : true
14221 Roo.data.DataProxy.superclass.constructor.call(this);
14224 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14227 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14231 * Ext JS Library 1.1.1
14232 * Copyright(c) 2006-2007, Ext JS, LLC.
14234 * Originally Released Under LGPL - original licence link has changed is not relivant.
14237 * <script type="text/javascript">
14240 * @class Roo.data.MemoryProxy
14241 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14242 * to the Reader when its load method is called.
14244 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14246 Roo.data.MemoryProxy = function(data){
14250 Roo.data.MemoryProxy.superclass.constructor.call(this);
14254 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14257 * Load data from the requested source (in this case an in-memory
14258 * data object passed to the constructor), read the data object into
14259 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14260 * process that block using the passed callback.
14261 * @param {Object} params This parameter is not used by the MemoryProxy class.
14262 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14263 * object into a block of Roo.data.Records.
14264 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14265 * The function must be passed <ul>
14266 * <li>The Record block object</li>
14267 * <li>The "arg" argument from the load function</li>
14268 * <li>A boolean success indicator</li>
14270 * @param {Object} scope The scope in which to call the callback
14271 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14273 load : function(params, reader, callback, scope, arg){
14274 params = params || {};
14277 result = reader.readRecords(params.data ? params.data :this.data);
14279 this.fireEvent("loadexception", this, arg, null, e);
14280 callback.call(scope, null, arg, false);
14283 callback.call(scope, result, arg, true);
14287 update : function(params, records){
14292 * Ext JS Library 1.1.1
14293 * Copyright(c) 2006-2007, Ext JS, LLC.
14295 * Originally Released Under LGPL - original licence link has changed is not relivant.
14298 * <script type="text/javascript">
14301 * @class Roo.data.HttpProxy
14302 * @extends Roo.data.DataProxy
14303 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14304 * configured to reference a certain URL.<br><br>
14306 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14307 * from which the running page was served.<br><br>
14309 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14311 * Be aware that to enable the browser to parse an XML document, the server must set
14312 * the Content-Type header in the HTTP response to "text/xml".
14314 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14315 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14316 * will be used to make the request.
14318 Roo.data.HttpProxy = function(conn){
14319 Roo.data.HttpProxy.superclass.constructor.call(this);
14320 // is conn a conn config or a real conn?
14322 this.useAjax = !conn || !conn.events;
14326 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14327 // thse are take from connection...
14330 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14333 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14334 * extra parameters to each request made by this object. (defaults to undefined)
14337 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14338 * to each request made by this object. (defaults to undefined)
14341 * @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)
14344 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14347 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14353 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14357 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14358 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14359 * a finer-grained basis than the DataProxy events.
14361 getConnection : function(){
14362 return this.useAjax ? Roo.Ajax : this.conn;
14366 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14367 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14368 * process that block using the passed callback.
14369 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14370 * for the request to the remote server.
14371 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14372 * object into a block of Roo.data.Records.
14373 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14374 * The function must be passed <ul>
14375 * <li>The Record block object</li>
14376 * <li>The "arg" argument from the load function</li>
14377 * <li>A boolean success indicator</li>
14379 * @param {Object} scope The scope in which to call the callback
14380 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14382 load : function(params, reader, callback, scope, arg){
14383 if(this.fireEvent("beforeload", this, params) !== false){
14385 params : params || {},
14387 callback : callback,
14392 callback : this.loadResponse,
14396 Roo.applyIf(o, this.conn);
14397 if(this.activeRequest){
14398 Roo.Ajax.abort(this.activeRequest);
14400 this.activeRequest = Roo.Ajax.request(o);
14402 this.conn.request(o);
14405 callback.call(scope||this, null, arg, false);
14410 loadResponse : function(o, success, response){
14411 delete this.activeRequest;
14413 this.fireEvent("loadexception", this, o, response);
14414 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14419 result = o.reader.read(response);
14421 this.fireEvent("loadexception", this, o, response, e);
14422 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14426 this.fireEvent("load", this, o, o.request.arg);
14427 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14431 update : function(dataSet){
14436 updateResponse : function(dataSet){
14441 * Ext JS Library 1.1.1
14442 * Copyright(c) 2006-2007, Ext JS, LLC.
14444 * Originally Released Under LGPL - original licence link has changed is not relivant.
14447 * <script type="text/javascript">
14451 * @class Roo.data.ScriptTagProxy
14452 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14453 * other than the originating domain of the running page.<br><br>
14455 * <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
14456 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14458 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14459 * source code that is used as the source inside a <script> tag.<br><br>
14461 * In order for the browser to process the returned data, the server must wrap the data object
14462 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14463 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14464 * depending on whether the callback name was passed:
14467 boolean scriptTag = false;
14468 String cb = request.getParameter("callback");
14471 response.setContentType("text/javascript");
14473 response.setContentType("application/x-json");
14475 Writer out = response.getWriter();
14477 out.write(cb + "(");
14479 out.print(dataBlock.toJsonString());
14486 * @param {Object} config A configuration object.
14488 Roo.data.ScriptTagProxy = function(config){
14489 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14490 Roo.apply(this, config);
14491 this.head = document.getElementsByTagName("head")[0];
14494 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14496 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14498 * @cfg {String} url The URL from which to request the data object.
14501 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14505 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14506 * the server the name of the callback function set up by the load call to process the returned data object.
14507 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14508 * javascript output which calls this named function passing the data object as its only parameter.
14510 callbackParam : "callback",
14512 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14513 * name to the request.
14518 * Load data from the configured URL, read the data object into
14519 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14520 * process that block using the passed callback.
14521 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14522 * for the request to the remote server.
14523 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14524 * object into a block of Roo.data.Records.
14525 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14526 * The function must be passed <ul>
14527 * <li>The Record block object</li>
14528 * <li>The "arg" argument from the load function</li>
14529 * <li>A boolean success indicator</li>
14531 * @param {Object} scope The scope in which to call the callback
14532 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14534 load : function(params, reader, callback, scope, arg){
14535 if(this.fireEvent("beforeload", this, params) !== false){
14537 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14539 var url = this.url;
14540 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14542 url += "&_dc=" + (new Date().getTime());
14544 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14547 cb : "stcCallback"+transId,
14548 scriptId : "stcScript"+transId,
14552 callback : callback,
14558 window[trans.cb] = function(o){
14559 conn.handleResponse(o, trans);
14562 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14564 if(this.autoAbort !== false){
14568 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14570 var script = document.createElement("script");
14571 script.setAttribute("src", url);
14572 script.setAttribute("type", "text/javascript");
14573 script.setAttribute("id", trans.scriptId);
14574 this.head.appendChild(script);
14576 this.trans = trans;
14578 callback.call(scope||this, null, arg, false);
14583 isLoading : function(){
14584 return this.trans ? true : false;
14588 * Abort the current server request.
14590 abort : function(){
14591 if(this.isLoading()){
14592 this.destroyTrans(this.trans);
14597 destroyTrans : function(trans, isLoaded){
14598 this.head.removeChild(document.getElementById(trans.scriptId));
14599 clearTimeout(trans.timeoutId);
14601 window[trans.cb] = undefined;
14603 delete window[trans.cb];
14606 // if hasn't been loaded, wait for load to remove it to prevent script error
14607 window[trans.cb] = function(){
14608 window[trans.cb] = undefined;
14610 delete window[trans.cb];
14617 handleResponse : function(o, trans){
14618 this.trans = false;
14619 this.destroyTrans(trans, true);
14622 result = trans.reader.readRecords(o);
14624 this.fireEvent("loadexception", this, o, trans.arg, e);
14625 trans.callback.call(trans.scope||window, null, trans.arg, false);
14628 this.fireEvent("load", this, o, trans.arg);
14629 trans.callback.call(trans.scope||window, result, trans.arg, true);
14633 handleFailure : function(trans){
14634 this.trans = false;
14635 this.destroyTrans(trans, false);
14636 this.fireEvent("loadexception", this, null, trans.arg);
14637 trans.callback.call(trans.scope||window, null, trans.arg, false);
14641 * Ext JS Library 1.1.1
14642 * Copyright(c) 2006-2007, Ext JS, LLC.
14644 * Originally Released Under LGPL - original licence link has changed is not relivant.
14647 * <script type="text/javascript">
14651 * @class Roo.data.JsonReader
14652 * @extends Roo.data.DataReader
14653 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14654 * based on mappings in a provided Roo.data.Record constructor.
14656 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14657 * in the reply previously.
14662 var RecordDef = Roo.data.Record.create([
14663 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14664 {name: 'occupation'} // This field will use "occupation" as the mapping.
14666 var myReader = new Roo.data.JsonReader({
14667 totalProperty: "results", // The property which contains the total dataset size (optional)
14668 root: "rows", // The property which contains an Array of row objects
14669 id: "id" // The property within each row object that provides an ID for the record (optional)
14673 * This would consume a JSON file like this:
14675 { 'results': 2, 'rows': [
14676 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14677 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14680 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14681 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14682 * paged from the remote server.
14683 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14684 * @cfg {String} root name of the property which contains the Array of row objects.
14685 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14686 * @cfg {Array} fields Array of field definition objects
14688 * Create a new JsonReader
14689 * @param {Object} meta Metadata configuration options
14690 * @param {Object} recordType Either an Array of field definition objects,
14691 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14693 Roo.data.JsonReader = function(meta, recordType){
14696 // set some defaults:
14697 Roo.applyIf(meta, {
14698 totalProperty: 'total',
14699 successProperty : 'success',
14704 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14706 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14708 readerType : 'Json',
14711 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14712 * Used by Store query builder to append _requestMeta to params.
14715 metaFromRemote : false,
14717 * This method is only used by a DataProxy which has retrieved data from a remote server.
14718 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14719 * @return {Object} data A data block which is used by an Roo.data.Store object as
14720 * a cache of Roo.data.Records.
14722 read : function(response){
14723 var json = response.responseText;
14725 var o = /* eval:var:o */ eval("("+json+")");
14727 throw {message: "JsonReader.read: Json object not found"};
14733 this.metaFromRemote = true;
14734 this.meta = o.metaData;
14735 this.recordType = Roo.data.Record.create(o.metaData.fields);
14736 this.onMetaChange(this.meta, this.recordType, o);
14738 return this.readRecords(o);
14741 // private function a store will implement
14742 onMetaChange : function(meta, recordType, o){
14749 simpleAccess: function(obj, subsc) {
14756 getJsonAccessor: function(){
14758 return function(expr) {
14760 return(re.test(expr))
14761 ? new Function("obj", "return obj." + expr)
14766 return Roo.emptyFn;
14771 * Create a data block containing Roo.data.Records from an XML document.
14772 * @param {Object} o An object which contains an Array of row objects in the property specified
14773 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14774 * which contains the total size of the dataset.
14775 * @return {Object} data A data block which is used by an Roo.data.Store object as
14776 * a cache of Roo.data.Records.
14778 readRecords : function(o){
14780 * After any data loads, the raw JSON data is available for further custom processing.
14784 var s = this.meta, Record = this.recordType,
14785 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14787 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14789 if(s.totalProperty) {
14790 this.getTotal = this.getJsonAccessor(s.totalProperty);
14792 if(s.successProperty) {
14793 this.getSuccess = this.getJsonAccessor(s.successProperty);
14795 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14797 var g = this.getJsonAccessor(s.id);
14798 this.getId = function(rec) {
14800 return (r === undefined || r === "") ? null : r;
14803 this.getId = function(){return null;};
14806 for(var jj = 0; jj < fl; jj++){
14808 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14809 this.ef[jj] = this.getJsonAccessor(map);
14813 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14814 if(s.totalProperty){
14815 var vt = parseInt(this.getTotal(o), 10);
14820 if(s.successProperty){
14821 var vs = this.getSuccess(o);
14822 if(vs === false || vs === 'false'){
14827 for(var i = 0; i < c; i++){
14830 var id = this.getId(n);
14831 for(var j = 0; j < fl; j++){
14833 var v = this.ef[j](n);
14835 Roo.log('missing convert for ' + f.name);
14839 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14841 var record = new Record(values, id);
14843 records[i] = record;
14849 totalRecords : totalRecords
14852 // used when loading children.. @see loadDataFromChildren
14853 toLoadData: function(rec)
14855 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14856 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14857 return { data : data, total : data.length };
14862 * Ext JS Library 1.1.1
14863 * Copyright(c) 2006-2007, Ext JS, LLC.
14865 * Originally Released Under LGPL - original licence link has changed is not relivant.
14868 * <script type="text/javascript">
14872 * @class Roo.data.ArrayReader
14873 * @extends Roo.data.DataReader
14874 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14875 * Each element of that Array represents a row of data fields. The
14876 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14877 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14881 var RecordDef = Roo.data.Record.create([
14882 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14883 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14885 var myReader = new Roo.data.ArrayReader({
14886 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14890 * This would consume an Array like this:
14892 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14896 * Create a new JsonReader
14897 * @param {Object} meta Metadata configuration options.
14898 * @param {Object|Array} recordType Either an Array of field definition objects
14900 * @cfg {Array} fields Array of field definition objects
14901 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14902 * as specified to {@link Roo.data.Record#create},
14903 * or an {@link Roo.data.Record} object
14906 * created using {@link Roo.data.Record#create}.
14908 Roo.data.ArrayReader = function(meta, recordType)
14910 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14913 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14916 * Create a data block containing Roo.data.Records from an XML document.
14917 * @param {Object} o An Array of row objects which represents the dataset.
14918 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14919 * a cache of Roo.data.Records.
14921 readRecords : function(o)
14923 var sid = this.meta ? this.meta.id : null;
14924 var recordType = this.recordType, fields = recordType.prototype.fields;
14927 for(var i = 0; i < root.length; i++){
14930 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14931 for(var j = 0, jlen = fields.length; j < jlen; j++){
14932 var f = fields.items[j];
14933 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14934 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14936 values[f.name] = v;
14938 var record = new recordType(values, id);
14940 records[records.length] = record;
14944 totalRecords : records.length
14947 // used when loading children.. @see loadDataFromChildren
14948 toLoadData: function(rec)
14950 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14951 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14962 * @class Roo.bootstrap.ComboBox
14963 * @extends Roo.bootstrap.TriggerField
14964 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14965 * @cfg {Boolean} append (true|false) default false
14966 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14967 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14968 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14969 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14970 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14971 * @cfg {Boolean} animate default true
14972 * @cfg {Boolean} emptyResultText only for touch device
14973 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14974 * @cfg {String} emptyTitle default ''
14975 * @cfg {Number} width fixed with? experimental
14977 * Create a new ComboBox.
14978 * @param {Object} config Configuration options
14980 Roo.bootstrap.ComboBox = function(config){
14981 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14985 * Fires when the dropdown list is expanded
14986 * @param {Roo.bootstrap.ComboBox} combo This combo box
14991 * Fires when the dropdown list is collapsed
14992 * @param {Roo.bootstrap.ComboBox} combo This combo box
14996 * @event beforeselect
14997 * Fires before a list item is selected. Return false to cancel the selection.
14998 * @param {Roo.bootstrap.ComboBox} combo This combo box
14999 * @param {Roo.data.Record} record The data record returned from the underlying store
15000 * @param {Number} index The index of the selected item in the dropdown list
15002 'beforeselect' : true,
15005 * Fires when a list item is selected
15006 * @param {Roo.bootstrap.ComboBox} combo This combo box
15007 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15008 * @param {Number} index The index of the selected item in the dropdown list
15012 * @event beforequery
15013 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15014 * The event object passed has these properties:
15015 * @param {Roo.bootstrap.ComboBox} combo This combo box
15016 * @param {String} query The query
15017 * @param {Boolean} forceAll true to force "all" query
15018 * @param {Boolean} cancel true to cancel the query
15019 * @param {Object} e The query event object
15021 'beforequery': true,
15024 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15025 * @param {Roo.bootstrap.ComboBox} combo This combo box
15030 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15031 * @param {Roo.bootstrap.ComboBox} combo This combo box
15032 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15037 * Fires when the remove value from the combobox array
15038 * @param {Roo.bootstrap.ComboBox} combo This combo box
15042 * @event afterremove
15043 * Fires when the remove value from the combobox array
15044 * @param {Roo.bootstrap.ComboBox} combo This combo box
15046 'afterremove' : true,
15048 * @event specialfilter
15049 * Fires when specialfilter
15050 * @param {Roo.bootstrap.ComboBox} combo This combo box
15052 'specialfilter' : true,
15055 * Fires when tick the element
15056 * @param {Roo.bootstrap.ComboBox} combo This combo box
15060 * @event touchviewdisplay
15061 * Fires when touch view require special display (default is using displayField)
15062 * @param {Roo.bootstrap.ComboBox} combo This combo box
15063 * @param {Object} cfg set html .
15065 'touchviewdisplay' : true
15070 this.tickItems = [];
15072 this.selectedIndex = -1;
15073 if(this.mode == 'local'){
15074 if(config.queryDelay === undefined){
15075 this.queryDelay = 10;
15077 if(config.minChars === undefined){
15083 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15086 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15087 * rendering into an Roo.Editor, defaults to false)
15090 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15091 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15094 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15097 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15098 * the dropdown list (defaults to undefined, with no header element)
15102 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15106 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15108 listWidth: undefined,
15110 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15111 * mode = 'remote' or 'text' if mode = 'local')
15113 displayField: undefined,
15116 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15117 * mode = 'remote' or 'value' if mode = 'local').
15118 * Note: use of a valueField requires the user make a selection
15119 * in order for a value to be mapped.
15121 valueField: undefined,
15123 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15128 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15129 * field's data value (defaults to the underlying DOM element's name)
15131 hiddenName: undefined,
15133 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15137 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15139 selectedClass: 'active',
15142 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15146 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15147 * anchor positions (defaults to 'tl-bl')
15149 listAlign: 'tl-bl?',
15151 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15155 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15156 * query specified by the allQuery config option (defaults to 'query')
15158 triggerAction: 'query',
15160 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15161 * (defaults to 4, does not apply if editable = false)
15165 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15166 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15170 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15171 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15175 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15176 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15180 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15181 * when editable = true (defaults to false)
15183 selectOnFocus:false,
15185 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15187 queryParam: 'query',
15189 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15190 * when mode = 'remote' (defaults to 'Loading...')
15192 loadingText: 'Loading...',
15194 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15198 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15202 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15203 * traditional select (defaults to true)
15207 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15211 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15215 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15216 * listWidth has a higher value)
15220 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15221 * allow the user to set arbitrary text into the field (defaults to false)
15223 forceSelection:false,
15225 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15226 * if typeAhead = true (defaults to 250)
15228 typeAheadDelay : 250,
15230 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15231 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15233 valueNotFoundText : undefined,
15235 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15237 blockFocus : false,
15240 * @cfg {Boolean} disableClear Disable showing of clear button.
15242 disableClear : false,
15244 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15246 alwaysQuery : false,
15249 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15254 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15256 invalidClass : "has-warning",
15259 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15261 validClass : "has-success",
15264 * @cfg {Boolean} specialFilter (true|false) special filter default false
15266 specialFilter : false,
15269 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15271 mobileTouchView : true,
15274 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15276 useNativeIOS : false,
15279 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15281 mobile_restrict_height : false,
15283 ios_options : false,
15295 btnPosition : 'right',
15296 triggerList : true,
15297 showToggleBtn : true,
15299 emptyResultText: 'Empty',
15300 triggerText : 'Select',
15304 // element that contains real text value.. (when hidden is used..)
15306 getAutoCreate : function()
15311 * Render classic select for iso
15314 if(Roo.isIOS && this.useNativeIOS){
15315 cfg = this.getAutoCreateNativeIOS();
15323 if(Roo.isTouch && this.mobileTouchView){
15324 cfg = this.getAutoCreateTouchView();
15331 if(!this.tickable){
15332 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15337 * ComboBox with tickable selections
15340 var align = this.labelAlign || this.parentLabelAlign();
15343 cls : 'form-group roo-combobox-tickable' //input-group
15346 var btn_text_select = '';
15347 var btn_text_done = '';
15348 var btn_text_cancel = '';
15350 if (this.btn_text_show) {
15351 btn_text_select = 'Select';
15352 btn_text_done = 'Done';
15353 btn_text_cancel = 'Cancel';
15358 cls : 'tickable-buttons',
15363 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15364 //html : this.triggerText
15365 html: btn_text_select
15371 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15373 html: btn_text_done
15379 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15381 html: btn_text_cancel
15387 buttons.cn.unshift({
15389 cls: 'roo-select2-search-field-input'
15395 Roo.each(buttons.cn, function(c){
15397 c.cls += ' btn-' + _this.size;
15400 if (_this.disabled) {
15407 style : 'display: contents',
15412 cls: 'form-hidden-field'
15416 cls: 'roo-select2-choices',
15420 cls: 'roo-select2-search-field',
15431 cls: 'roo-select2-container input-group roo-select2-container-multi',
15437 // cls: 'typeahead typeahead-long dropdown-menu',
15438 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15443 if(this.hasFeedback && !this.allowBlank){
15447 cls: 'glyphicon form-control-feedback'
15450 combobox.cn.push(feedback);
15457 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15458 tooltip : 'This field is required'
15460 if (Roo.bootstrap.version == 4) {
15463 style : 'display:none'
15466 if (align ==='left' && this.fieldLabel.length) {
15468 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15475 cls : 'control-label col-form-label',
15476 html : this.fieldLabel
15488 var labelCfg = cfg.cn[1];
15489 var contentCfg = cfg.cn[2];
15492 if(this.indicatorpos == 'right'){
15498 cls : 'control-label col-form-label',
15502 html : this.fieldLabel
15518 labelCfg = cfg.cn[0];
15519 contentCfg = cfg.cn[1];
15523 if(this.labelWidth > 12){
15524 labelCfg.style = "width: " + this.labelWidth + 'px';
15526 if(this.width * 1 > 0){
15527 contentCfg.style = "width: " + this.width + 'px';
15529 if(this.labelWidth < 13 && this.labelmd == 0){
15530 this.labelmd = this.labelWidth;
15533 if(this.labellg > 0){
15534 labelCfg.cls += ' col-lg-' + this.labellg;
15535 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15538 if(this.labelmd > 0){
15539 labelCfg.cls += ' col-md-' + this.labelmd;
15540 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15543 if(this.labelsm > 0){
15544 labelCfg.cls += ' col-sm-' + this.labelsm;
15545 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15548 if(this.labelxs > 0){
15549 labelCfg.cls += ' col-xs-' + this.labelxs;
15550 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15554 } else if ( this.fieldLabel.length) {
15555 // Roo.log(" label");
15560 //cls : 'input-group-addon',
15561 html : this.fieldLabel
15566 if(this.indicatorpos == 'right'){
15570 //cls : 'input-group-addon',
15571 html : this.fieldLabel
15581 // Roo.log(" no label && no align");
15588 ['xs','sm','md','lg'].map(function(size){
15589 if (settings[size]) {
15590 cfg.cls += ' col-' + size + '-' + settings[size];
15598 _initEventsCalled : false,
15601 initEvents: function()
15603 if (this._initEventsCalled) { // as we call render... prevent looping...
15606 this._initEventsCalled = true;
15609 throw "can not find store for combo";
15612 this.indicator = this.indicatorEl();
15614 this.store = Roo.factory(this.store, Roo.data);
15615 this.store.parent = this;
15617 // if we are building from html. then this element is so complex, that we can not really
15618 // use the rendered HTML.
15619 // so we have to trash and replace the previous code.
15620 if (Roo.XComponent.build_from_html) {
15621 // remove this element....
15622 var e = this.el.dom, k=0;
15623 while (e ) { e = e.previousSibling; ++k;}
15628 this.rendered = false;
15630 this.render(this.parent().getChildContainer(true), k);
15633 if(Roo.isIOS && this.useNativeIOS){
15634 this.initIOSView();
15642 if(Roo.isTouch && this.mobileTouchView){
15643 this.initTouchView();
15648 this.initTickableEvents();
15652 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15654 if(this.hiddenName){
15656 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15658 this.hiddenField.dom.value =
15659 this.hiddenValue !== undefined ? this.hiddenValue :
15660 this.value !== undefined ? this.value : '';
15662 // prevent input submission
15663 this.el.dom.removeAttribute('name');
15664 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15669 // this.el.dom.setAttribute('autocomplete', 'off');
15672 var cls = 'x-combo-list';
15674 //this.list = new Roo.Layer({
15675 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15681 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15682 _this.list.setWidth(lw);
15685 this.list.on('mouseover', this.onViewOver, this);
15686 this.list.on('mousemove', this.onViewMove, this);
15687 this.list.on('scroll', this.onViewScroll, this);
15690 this.list.swallowEvent('mousewheel');
15691 this.assetHeight = 0;
15694 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15695 this.assetHeight += this.header.getHeight();
15698 this.innerList = this.list.createChild({cls:cls+'-inner'});
15699 this.innerList.on('mouseover', this.onViewOver, this);
15700 this.innerList.on('mousemove', this.onViewMove, this);
15701 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15703 if(this.allowBlank && !this.pageSize && !this.disableClear){
15704 this.footer = this.list.createChild({cls:cls+'-ft'});
15705 this.pageTb = new Roo.Toolbar(this.footer);
15709 this.footer = this.list.createChild({cls:cls+'-ft'});
15710 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15711 {pageSize: this.pageSize});
15715 if (this.pageTb && this.allowBlank && !this.disableClear) {
15717 this.pageTb.add(new Roo.Toolbar.Fill(), {
15718 cls: 'x-btn-icon x-btn-clear',
15720 handler: function()
15723 _this.clearValue();
15724 _this.onSelect(false, -1);
15729 this.assetHeight += this.footer.getHeight();
15734 this.tpl = Roo.bootstrap.version == 4 ?
15735 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15736 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15739 this.view = new Roo.View(this.list, this.tpl, {
15740 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15742 //this.view.wrapEl.setDisplayed(false);
15743 this.view.on('click', this.onViewClick, this);
15746 this.store.on('beforeload', this.onBeforeLoad, this);
15747 this.store.on('load', this.onLoad, this);
15748 this.store.on('loadexception', this.onLoadException, this);
15750 if(this.resizable){
15751 this.resizer = new Roo.Resizable(this.list, {
15752 pinned:true, handles:'se'
15754 this.resizer.on('resize', function(r, w, h){
15755 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15756 this.listWidth = w;
15757 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15758 this.restrictHeight();
15760 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15763 if(!this.editable){
15764 this.editable = true;
15765 this.setEditable(false);
15770 if (typeof(this.events.add.listeners) != 'undefined') {
15772 this.addicon = this.wrap.createChild(
15773 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15775 this.addicon.on('click', function(e) {
15776 this.fireEvent('add', this);
15779 if (typeof(this.events.edit.listeners) != 'undefined') {
15781 this.editicon = this.wrap.createChild(
15782 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15783 if (this.addicon) {
15784 this.editicon.setStyle('margin-left', '40px');
15786 this.editicon.on('click', function(e) {
15788 // we fire even if inothing is selected..
15789 this.fireEvent('edit', this, this.lastData );
15795 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15796 "up" : function(e){
15797 this.inKeyMode = true;
15801 "down" : function(e){
15802 if(!this.isExpanded()){
15803 this.onTriggerClick();
15805 this.inKeyMode = true;
15810 "enter" : function(e){
15811 // this.onViewClick();
15815 if(this.fireEvent("specialkey", this, e)){
15816 this.onViewClick(false);
15822 "esc" : function(e){
15826 "tab" : function(e){
15829 if(this.fireEvent("specialkey", this, e)){
15830 this.onViewClick(false);
15838 doRelay : function(foo, bar, hname){
15839 if(hname == 'down' || this.scope.isExpanded()){
15840 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15849 this.queryDelay = Math.max(this.queryDelay || 10,
15850 this.mode == 'local' ? 10 : 250);
15853 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15855 if(this.typeAhead){
15856 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15858 if(this.editable !== false){
15859 this.inputEl().on("keyup", this.onKeyUp, this);
15861 if(this.forceSelection){
15862 this.inputEl().on('blur', this.doForce, this);
15866 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15867 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15871 initTickableEvents: function()
15875 if(this.hiddenName){
15877 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15879 this.hiddenField.dom.value =
15880 this.hiddenValue !== undefined ? this.hiddenValue :
15881 this.value !== undefined ? this.value : '';
15883 // prevent input submission
15884 this.el.dom.removeAttribute('name');
15885 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15890 // this.list = this.el.select('ul.dropdown-menu',true).first();
15892 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15893 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15894 if(this.triggerList){
15895 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15898 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15899 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15901 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15902 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15904 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15905 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15907 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15908 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15909 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15912 this.cancelBtn.hide();
15917 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15918 _this.list.setWidth(lw);
15921 this.list.on('mouseover', this.onViewOver, this);
15922 this.list.on('mousemove', this.onViewMove, this);
15924 this.list.on('scroll', this.onViewScroll, this);
15927 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15928 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15931 this.view = new Roo.View(this.list, this.tpl, {
15936 selectedClass: this.selectedClass
15939 //this.view.wrapEl.setDisplayed(false);
15940 this.view.on('click', this.onViewClick, this);
15944 this.store.on('beforeload', this.onBeforeLoad, this);
15945 this.store.on('load', this.onLoad, this);
15946 this.store.on('loadexception', this.onLoadException, this);
15949 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15950 "up" : function(e){
15951 this.inKeyMode = true;
15955 "down" : function(e){
15956 this.inKeyMode = true;
15960 "enter" : function(e){
15961 if(this.fireEvent("specialkey", this, e)){
15962 this.onViewClick(false);
15968 "esc" : function(e){
15969 this.onTickableFooterButtonClick(e, false, false);
15972 "tab" : function(e){
15973 this.fireEvent("specialkey", this, e);
15975 this.onTickableFooterButtonClick(e, false, false);
15982 doRelay : function(e, fn, key){
15983 if(this.scope.isExpanded()){
15984 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15993 this.queryDelay = Math.max(this.queryDelay || 10,
15994 this.mode == 'local' ? 10 : 250);
15997 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15999 if(this.typeAhead){
16000 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16003 if(this.editable !== false){
16004 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16007 this.indicator = this.indicatorEl();
16009 if(this.indicator){
16010 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16011 this.indicator.hide();
16016 onDestroy : function(){
16018 this.view.setStore(null);
16019 this.view.el.removeAllListeners();
16020 this.view.el.remove();
16021 this.view.purgeListeners();
16024 this.list.dom.innerHTML = '';
16028 this.store.un('beforeload', this.onBeforeLoad, this);
16029 this.store.un('load', this.onLoad, this);
16030 this.store.un('loadexception', this.onLoadException, this);
16032 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16036 fireKey : function(e){
16037 if(e.isNavKeyPress() && !this.list.isVisible()){
16038 this.fireEvent("specialkey", this, e);
16043 onResize: function(w, h)
16047 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16049 // if(typeof w != 'number'){
16050 // // we do not handle it!?!?
16053 // var tw = this.trigger.getWidth();
16054 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16055 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16057 // this.inputEl().setWidth( this.adjustWidth('input', x));
16059 // //this.trigger.setStyle('left', x+'px');
16061 // if(this.list && this.listWidth === undefined){
16062 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16063 // this.list.setWidth(lw);
16064 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16072 * Allow or prevent the user from directly editing the field text. If false is passed,
16073 * the user will only be able to select from the items defined in the dropdown list. This method
16074 * is the runtime equivalent of setting the 'editable' config option at config time.
16075 * @param {Boolean} value True to allow the user to directly edit the field text
16077 setEditable : function(value){
16078 if(value == this.editable){
16081 this.editable = value;
16083 this.inputEl().dom.setAttribute('readOnly', true);
16084 this.inputEl().on('mousedown', this.onTriggerClick, this);
16085 this.inputEl().addClass('x-combo-noedit');
16087 this.inputEl().dom.setAttribute('readOnly', false);
16088 this.inputEl().un('mousedown', this.onTriggerClick, this);
16089 this.inputEl().removeClass('x-combo-noedit');
16095 onBeforeLoad : function(combo,opts){
16096 if(!this.hasFocus){
16100 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16102 this.restrictHeight();
16103 this.selectedIndex = -1;
16107 onLoad : function(){
16109 this.hasQuery = false;
16111 if(!this.hasFocus){
16115 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16116 this.loading.hide();
16119 if(this.store.getCount() > 0){
16122 this.restrictHeight();
16123 if(this.lastQuery == this.allQuery){
16124 if(this.editable && !this.tickable){
16125 this.inputEl().dom.select();
16129 !this.selectByValue(this.value, true) &&
16132 !this.store.lastOptions ||
16133 typeof(this.store.lastOptions.add) == 'undefined' ||
16134 this.store.lastOptions.add != true
16137 this.select(0, true);
16140 if(this.autoFocus){
16143 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16144 this.taTask.delay(this.typeAheadDelay);
16148 this.onEmptyResults();
16154 onLoadException : function()
16156 this.hasQuery = false;
16158 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16159 this.loading.hide();
16162 if(this.tickable && this.editable){
16167 // only causes errors at present
16168 //Roo.log(this.store.reader.jsonData);
16169 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16171 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16177 onTypeAhead : function(){
16178 if(this.store.getCount() > 0){
16179 var r = this.store.getAt(0);
16180 var newValue = r.data[this.displayField];
16181 var len = newValue.length;
16182 var selStart = this.getRawValue().length;
16184 if(selStart != len){
16185 this.setRawValue(newValue);
16186 this.selectText(selStart, newValue.length);
16192 onSelect : function(record, index){
16194 if(this.fireEvent('beforeselect', this, record, index) !== false){
16196 this.setFromData(index > -1 ? record.data : false);
16199 this.fireEvent('select', this, record, index);
16204 * Returns the currently selected field value or empty string if no value is set.
16205 * @return {String} value The selected value
16207 getValue : function()
16209 if(Roo.isIOS && this.useNativeIOS){
16210 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16214 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16217 if(this.valueField){
16218 return typeof this.value != 'undefined' ? this.value : '';
16220 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16224 getRawValue : function()
16226 if(Roo.isIOS && this.useNativeIOS){
16227 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16230 var v = this.inputEl().getValue();
16236 * Clears any text/value currently set in the field
16238 clearValue : function(){
16240 if(this.hiddenField){
16241 this.hiddenField.dom.value = '';
16244 this.setRawValue('');
16245 this.lastSelectionText = '';
16246 this.lastData = false;
16248 var close = this.closeTriggerEl();
16259 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16260 * will be displayed in the field. If the value does not match the data value of an existing item,
16261 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16262 * Otherwise the field will be blank (although the value will still be set).
16263 * @param {String} value The value to match
16265 setValue : function(v)
16267 if(Roo.isIOS && this.useNativeIOS){
16268 this.setIOSValue(v);
16278 if(this.valueField){
16279 var r = this.findRecord(this.valueField, v);
16281 text = r.data[this.displayField];
16282 }else if(this.valueNotFoundText !== undefined){
16283 text = this.valueNotFoundText;
16286 this.lastSelectionText = text;
16287 if(this.hiddenField){
16288 this.hiddenField.dom.value = v;
16290 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16293 var close = this.closeTriggerEl();
16296 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16302 * @property {Object} the last set data for the element
16307 * Sets the value of the field based on a object which is related to the record format for the store.
16308 * @param {Object} value the value to set as. or false on reset?
16310 setFromData : function(o){
16317 var dv = ''; // display value
16318 var vv = ''; // value value..
16320 if (this.displayField) {
16321 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16323 // this is an error condition!!!
16324 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16327 if(this.valueField){
16328 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16331 var close = this.closeTriggerEl();
16334 if(dv.length || vv * 1 > 0){
16336 this.blockFocus=true;
16342 if(this.hiddenField){
16343 this.hiddenField.dom.value = vv;
16345 this.lastSelectionText = dv;
16346 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16350 // no hidden field.. - we store the value in 'value', but still display
16351 // display field!!!!
16352 this.lastSelectionText = dv;
16353 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16360 reset : function(){
16361 // overridden so that last data is reset..
16368 this.setValue(this.originalValue);
16369 //this.clearInvalid();
16370 this.lastData = false;
16372 this.view.clearSelections();
16378 findRecord : function(prop, value){
16380 if(this.store.getCount() > 0){
16381 this.store.each(function(r){
16382 if(r.data[prop] == value){
16392 getName: function()
16394 // returns hidden if it's set..
16395 if (!this.rendered) {return ''};
16396 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16400 onViewMove : function(e, t){
16401 this.inKeyMode = false;
16405 onViewOver : function(e, t){
16406 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16409 var item = this.view.findItemFromChild(t);
16412 var index = this.view.indexOf(item);
16413 this.select(index, false);
16418 onViewClick : function(view, doFocus, el, e)
16420 var index = this.view.getSelectedIndexes()[0];
16422 var r = this.store.getAt(index);
16426 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16433 Roo.each(this.tickItems, function(v,k){
16435 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16437 _this.tickItems.splice(k, 1);
16439 if(typeof(e) == 'undefined' && view == false){
16440 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16452 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16453 this.tickItems.push(r.data);
16456 if(typeof(e) == 'undefined' && view == false){
16457 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16464 this.onSelect(r, index);
16466 if(doFocus !== false && !this.blockFocus){
16467 this.inputEl().focus();
16472 restrictHeight : function(){
16473 //this.innerList.dom.style.height = '';
16474 //var inner = this.innerList.dom;
16475 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16476 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16477 //this.list.beginUpdate();
16478 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16479 this.list.alignTo(this.inputEl(), this.listAlign);
16480 this.list.alignTo(this.inputEl(), this.listAlign);
16481 //this.list.endUpdate();
16485 onEmptyResults : function(){
16487 if(this.tickable && this.editable){
16488 this.hasFocus = false;
16489 this.restrictHeight();
16497 * Returns true if the dropdown list is expanded, else false.
16499 isExpanded : function(){
16500 return this.list.isVisible();
16504 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16505 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16506 * @param {String} value The data value of the item to select
16507 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16508 * selected item if it is not currently in view (defaults to true)
16509 * @return {Boolean} True if the value matched an item in the list, else false
16511 selectByValue : function(v, scrollIntoView){
16512 if(v !== undefined && v !== null){
16513 var r = this.findRecord(this.valueField || this.displayField, v);
16515 this.select(this.store.indexOf(r), scrollIntoView);
16523 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16524 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16525 * @param {Number} index The zero-based index of the list item to select
16526 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16527 * selected item if it is not currently in view (defaults to true)
16529 select : function(index, scrollIntoView){
16530 this.selectedIndex = index;
16531 this.view.select(index);
16532 if(scrollIntoView !== false){
16533 var el = this.view.getNode(index);
16535 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16538 this.list.scrollChildIntoView(el, false);
16544 selectNext : function(){
16545 var ct = this.store.getCount();
16547 if(this.selectedIndex == -1){
16549 }else if(this.selectedIndex < ct-1){
16550 this.select(this.selectedIndex+1);
16556 selectPrev : function(){
16557 var ct = this.store.getCount();
16559 if(this.selectedIndex == -1){
16561 }else if(this.selectedIndex != 0){
16562 this.select(this.selectedIndex-1);
16568 onKeyUp : function(e){
16569 if(this.editable !== false && !e.isSpecialKey()){
16570 this.lastKey = e.getKey();
16571 this.dqTask.delay(this.queryDelay);
16576 validateBlur : function(){
16577 return !this.list || !this.list.isVisible();
16581 initQuery : function(){
16583 var v = this.getRawValue();
16585 if(this.tickable && this.editable){
16586 v = this.tickableInputEl().getValue();
16593 doForce : function(){
16594 if(this.inputEl().dom.value.length > 0){
16595 this.inputEl().dom.value =
16596 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16602 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16603 * query allowing the query action to be canceled if needed.
16604 * @param {String} query The SQL query to execute
16605 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16606 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16607 * saved in the current store (defaults to false)
16609 doQuery : function(q, forceAll){
16611 if(q === undefined || q === null){
16616 forceAll: forceAll,
16620 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16625 forceAll = qe.forceAll;
16626 if(forceAll === true || (q.length >= this.minChars)){
16628 this.hasQuery = true;
16630 if(this.lastQuery != q || this.alwaysQuery){
16631 this.lastQuery = q;
16632 if(this.mode == 'local'){
16633 this.selectedIndex = -1;
16635 this.store.clearFilter();
16638 if(this.specialFilter){
16639 this.fireEvent('specialfilter', this);
16644 this.store.filter(this.displayField, q);
16647 this.store.fireEvent("datachanged", this.store);
16654 this.store.baseParams[this.queryParam] = q;
16656 var options = {params : this.getParams(q)};
16659 options.add = true;
16660 options.params.start = this.page * this.pageSize;
16663 this.store.load(options);
16666 * this code will make the page width larger, at the beginning, the list not align correctly,
16667 * we should expand the list on onLoad
16668 * so command out it
16673 this.selectedIndex = -1;
16678 this.loadNext = false;
16682 getParams : function(q){
16684 //p[this.queryParam] = q;
16688 p.limit = this.pageSize;
16694 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16696 collapse : function(){
16697 if(!this.isExpanded()){
16703 this.hasFocus = false;
16707 this.cancelBtn.hide();
16708 this.trigger.show();
16711 this.tickableInputEl().dom.value = '';
16712 this.tickableInputEl().blur();
16717 Roo.get(document).un('mousedown', this.collapseIf, this);
16718 Roo.get(document).un('mousewheel', this.collapseIf, this);
16719 if (!this.editable) {
16720 Roo.get(document).un('keydown', this.listKeyPress, this);
16722 this.fireEvent('collapse', this);
16728 collapseIf : function(e){
16729 var in_combo = e.within(this.el);
16730 var in_list = e.within(this.list);
16731 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16733 if (in_combo || in_list || is_list) {
16734 //e.stopPropagation();
16739 this.onTickableFooterButtonClick(e, false, false);
16747 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16749 expand : function(){
16751 if(this.isExpanded() || !this.hasFocus){
16755 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16756 this.list.setWidth(lw);
16762 this.restrictHeight();
16766 this.tickItems = Roo.apply([], this.item);
16769 this.cancelBtn.show();
16770 this.trigger.hide();
16773 this.tickableInputEl().focus();
16778 Roo.get(document).on('mousedown', this.collapseIf, this);
16779 Roo.get(document).on('mousewheel', this.collapseIf, this);
16780 if (!this.editable) {
16781 Roo.get(document).on('keydown', this.listKeyPress, this);
16784 this.fireEvent('expand', this);
16788 // Implements the default empty TriggerField.onTriggerClick function
16789 onTriggerClick : function(e)
16791 Roo.log('trigger click');
16793 if(this.disabled || !this.triggerList){
16798 this.loadNext = false;
16800 if(this.isExpanded()){
16802 if (!this.blockFocus) {
16803 this.inputEl().focus();
16807 this.hasFocus = true;
16808 if(this.triggerAction == 'all') {
16809 this.doQuery(this.allQuery, true);
16811 this.doQuery(this.getRawValue());
16813 if (!this.blockFocus) {
16814 this.inputEl().focus();
16819 onTickableTriggerClick : function(e)
16826 this.loadNext = false;
16827 this.hasFocus = true;
16829 if(this.triggerAction == 'all') {
16830 this.doQuery(this.allQuery, true);
16832 this.doQuery(this.getRawValue());
16836 onSearchFieldClick : function(e)
16838 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16839 this.onTickableFooterButtonClick(e, false, false);
16843 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16848 this.loadNext = false;
16849 this.hasFocus = true;
16851 if(this.triggerAction == 'all') {
16852 this.doQuery(this.allQuery, true);
16854 this.doQuery(this.getRawValue());
16858 listKeyPress : function(e)
16860 //Roo.log('listkeypress');
16861 // scroll to first matching element based on key pres..
16862 if (e.isSpecialKey()) {
16865 var k = String.fromCharCode(e.getKey()).toUpperCase();
16868 var csel = this.view.getSelectedNodes();
16869 var cselitem = false;
16871 var ix = this.view.indexOf(csel[0]);
16872 cselitem = this.store.getAt(ix);
16873 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16879 this.store.each(function(v) {
16881 // start at existing selection.
16882 if (cselitem.id == v.id) {
16888 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16889 match = this.store.indexOf(v);
16895 if (match === false) {
16896 return true; // no more action?
16899 this.view.select(match);
16900 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16901 sn.scrollIntoView(sn.dom.parentNode, false);
16904 onViewScroll : function(e, t){
16906 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){
16910 this.hasQuery = true;
16912 this.loading = this.list.select('.loading', true).first();
16914 if(this.loading === null){
16915 this.list.createChild({
16917 cls: 'loading roo-select2-more-results roo-select2-active',
16918 html: 'Loading more results...'
16921 this.loading = this.list.select('.loading', true).first();
16923 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16925 this.loading.hide();
16928 this.loading.show();
16933 this.loadNext = true;
16935 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16940 addItem : function(o)
16942 var dv = ''; // display value
16944 if (this.displayField) {
16945 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16947 // this is an error condition!!!
16948 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16955 var choice = this.choices.createChild({
16957 cls: 'roo-select2-search-choice',
16966 cls: 'roo-select2-search-choice-close fa fa-times',
16971 }, this.searchField);
16973 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16975 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16983 this.inputEl().dom.value = '';
16988 onRemoveItem : function(e, _self, o)
16990 e.preventDefault();
16992 this.lastItem = Roo.apply([], this.item);
16994 var index = this.item.indexOf(o.data) * 1;
16997 Roo.log('not this item?!');
17001 this.item.splice(index, 1);
17006 this.fireEvent('remove', this, e);
17012 syncValue : function()
17014 if(!this.item.length){
17021 Roo.each(this.item, function(i){
17022 if(_this.valueField){
17023 value.push(i[_this.valueField]);
17030 this.value = value.join(',');
17032 if(this.hiddenField){
17033 this.hiddenField.dom.value = this.value;
17036 this.store.fireEvent("datachanged", this.store);
17041 clearItem : function()
17043 if(!this.multiple){
17049 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17057 if(this.tickable && !Roo.isTouch){
17058 this.view.refresh();
17062 inputEl: function ()
17064 if(Roo.isIOS && this.useNativeIOS){
17065 return this.el.select('select.roo-ios-select', true).first();
17068 if(Roo.isTouch && this.mobileTouchView){
17069 return this.el.select('input.form-control',true).first();
17073 return this.searchField;
17076 return this.el.select('input.form-control',true).first();
17079 onTickableFooterButtonClick : function(e, btn, el)
17081 e.preventDefault();
17083 this.lastItem = Roo.apply([], this.item);
17085 if(btn && btn.name == 'cancel'){
17086 this.tickItems = Roo.apply([], this.item);
17095 Roo.each(this.tickItems, function(o){
17103 validate : function()
17105 if(this.getVisibilityEl().hasClass('hidden')){
17109 var v = this.getRawValue();
17112 v = this.getValue();
17115 if(this.disabled || this.allowBlank || v.length){
17120 this.markInvalid();
17124 tickableInputEl : function()
17126 if(!this.tickable || !this.editable){
17127 return this.inputEl();
17130 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17134 getAutoCreateTouchView : function()
17139 cls: 'form-group' //input-group
17145 type : this.inputType,
17146 cls : 'form-control x-combo-noedit',
17147 autocomplete: 'new-password',
17148 placeholder : this.placeholder || '',
17153 input.name = this.name;
17157 input.cls += ' input-' + this.size;
17160 if (this.disabled) {
17161 input.disabled = true;
17165 cls : 'roo-combobox-wrap',
17172 inputblock.cls += ' input-group';
17174 inputblock.cn.unshift({
17176 cls : 'input-group-addon input-group-prepend input-group-text',
17181 if(this.removable && !this.multiple){
17182 inputblock.cls += ' roo-removable';
17184 inputblock.cn.push({
17187 cls : 'roo-combo-removable-btn close'
17191 if(this.hasFeedback && !this.allowBlank){
17193 inputblock.cls += ' has-feedback';
17195 inputblock.cn.push({
17197 cls: 'glyphicon form-control-feedback'
17204 inputblock.cls += (this.before) ? '' : ' input-group';
17206 inputblock.cn.push({
17208 cls : 'input-group-addon input-group-append input-group-text',
17214 var ibwrap = inputblock;
17219 cls: 'roo-select2-choices',
17223 cls: 'roo-select2-search-field',
17236 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17241 cls: 'form-hidden-field'
17247 if(!this.multiple && this.showToggleBtn){
17253 if (this.caret != false) {
17256 cls: 'fa fa-' + this.caret
17263 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17265 Roo.bootstrap.version == 3 ? caret : '',
17268 cls: 'combobox-clear',
17282 combobox.cls += ' roo-select2-container-multi';
17285 var align = this.labelAlign || this.parentLabelAlign();
17287 if (align ==='left' && this.fieldLabel.length) {
17292 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17293 tooltip : 'This field is required'
17297 cls : 'control-label col-form-label',
17298 html : this.fieldLabel
17302 cls : 'roo-combobox-wrap ',
17309 var labelCfg = cfg.cn[1];
17310 var contentCfg = cfg.cn[2];
17313 if(this.indicatorpos == 'right'){
17318 cls : 'control-label col-form-label',
17322 html : this.fieldLabel
17326 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17327 tooltip : 'This field is required'
17332 cls : "roo-combobox-wrap ",
17340 labelCfg = cfg.cn[0];
17341 contentCfg = cfg.cn[1];
17346 if(this.labelWidth > 12){
17347 labelCfg.style = "width: " + this.labelWidth + 'px';
17350 if(this.labelWidth < 13 && this.labelmd == 0){
17351 this.labelmd = this.labelWidth;
17354 if(this.labellg > 0){
17355 labelCfg.cls += ' col-lg-' + this.labellg;
17356 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17359 if(this.labelmd > 0){
17360 labelCfg.cls += ' col-md-' + this.labelmd;
17361 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17364 if(this.labelsm > 0){
17365 labelCfg.cls += ' col-sm-' + this.labelsm;
17366 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17369 if(this.labelxs > 0){
17370 labelCfg.cls += ' col-xs-' + this.labelxs;
17371 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17375 } else if ( this.fieldLabel.length) {
17379 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17380 tooltip : 'This field is required'
17384 cls : 'control-label',
17385 html : this.fieldLabel
17396 if(this.indicatorpos == 'right'){
17400 cls : 'control-label',
17401 html : this.fieldLabel,
17405 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17406 tooltip : 'This field is required'
17423 var settings = this;
17425 ['xs','sm','md','lg'].map(function(size){
17426 if (settings[size]) {
17427 cfg.cls += ' col-' + size + '-' + settings[size];
17434 initTouchView : function()
17436 this.renderTouchView();
17438 this.touchViewEl.on('scroll', function(){
17439 this.el.dom.scrollTop = 0;
17442 this.originalValue = this.getValue();
17444 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17446 this.inputEl().on("click", this.showTouchView, this);
17447 if (this.triggerEl) {
17448 this.triggerEl.on("click", this.showTouchView, this);
17452 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17453 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17455 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17457 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17458 this.store.on('load', this.onTouchViewLoad, this);
17459 this.store.on('loadexception', this.onTouchViewLoadException, this);
17461 if(this.hiddenName){
17463 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17465 this.hiddenField.dom.value =
17466 this.hiddenValue !== undefined ? this.hiddenValue :
17467 this.value !== undefined ? this.value : '';
17469 this.el.dom.removeAttribute('name');
17470 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17474 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17475 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17478 if(this.removable && !this.multiple){
17479 var close = this.closeTriggerEl();
17481 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17482 close.on('click', this.removeBtnClick, this, close);
17486 * fix the bug in Safari iOS8
17488 this.inputEl().on("focus", function(e){
17489 document.activeElement.blur();
17492 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17499 renderTouchView : function()
17501 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17502 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17504 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17505 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17507 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17508 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17509 this.touchViewBodyEl.setStyle('overflow', 'auto');
17511 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17512 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17514 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17515 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17519 showTouchView : function()
17525 this.touchViewHeaderEl.hide();
17527 if(this.modalTitle.length){
17528 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17529 this.touchViewHeaderEl.show();
17532 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17533 this.touchViewEl.show();
17535 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17537 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17538 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17540 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17542 if(this.modalTitle.length){
17543 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17546 this.touchViewBodyEl.setHeight(bodyHeight);
17550 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17552 this.touchViewEl.addClass(['in','show']);
17555 if(this._touchViewMask){
17556 Roo.get(document.body).addClass("x-body-masked");
17557 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17558 this._touchViewMask.setStyle('z-index', 10000);
17559 this._touchViewMask.addClass('show');
17562 this.doTouchViewQuery();
17566 hideTouchView : function()
17568 this.touchViewEl.removeClass(['in','show']);
17572 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17574 this.touchViewEl.setStyle('display', 'none');
17577 if(this._touchViewMask){
17578 this._touchViewMask.removeClass('show');
17579 Roo.get(document.body).removeClass("x-body-masked");
17583 setTouchViewValue : function()
17590 Roo.each(this.tickItems, function(o){
17595 this.hideTouchView();
17598 doTouchViewQuery : function()
17607 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17611 if(!this.alwaysQuery || this.mode == 'local'){
17612 this.onTouchViewLoad();
17619 onTouchViewBeforeLoad : function(combo,opts)
17625 onTouchViewLoad : function()
17627 if(this.store.getCount() < 1){
17628 this.onTouchViewEmptyResults();
17632 this.clearTouchView();
17634 var rawValue = this.getRawValue();
17636 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17638 this.tickItems = [];
17640 this.store.data.each(function(d, rowIndex){
17641 var row = this.touchViewListGroup.createChild(template);
17643 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17644 row.addClass(d.data.cls);
17647 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17650 html : d.data[this.displayField]
17653 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17654 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17657 row.removeClass('selected');
17658 if(!this.multiple && this.valueField &&
17659 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17662 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17663 row.addClass('selected');
17666 if(this.multiple && this.valueField &&
17667 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17671 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17672 this.tickItems.push(d.data);
17675 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17679 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17681 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17683 if(this.modalTitle.length){
17684 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17687 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17689 if(this.mobile_restrict_height && listHeight < bodyHeight){
17690 this.touchViewBodyEl.setHeight(listHeight);
17695 if(firstChecked && listHeight > bodyHeight){
17696 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17701 onTouchViewLoadException : function()
17703 this.hideTouchView();
17706 onTouchViewEmptyResults : function()
17708 this.clearTouchView();
17710 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17712 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17716 clearTouchView : function()
17718 this.touchViewListGroup.dom.innerHTML = '';
17721 onTouchViewClick : function(e, el, o)
17723 e.preventDefault();
17726 var rowIndex = o.rowIndex;
17728 var r = this.store.getAt(rowIndex);
17730 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17732 if(!this.multiple){
17733 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17734 c.dom.removeAttribute('checked');
17737 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17739 this.setFromData(r.data);
17741 var close = this.closeTriggerEl();
17747 this.hideTouchView();
17749 this.fireEvent('select', this, r, rowIndex);
17754 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17755 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17756 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17760 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17761 this.addItem(r.data);
17762 this.tickItems.push(r.data);
17766 getAutoCreateNativeIOS : function()
17769 cls: 'form-group' //input-group,
17774 cls : 'roo-ios-select'
17778 combobox.name = this.name;
17781 if (this.disabled) {
17782 combobox.disabled = true;
17785 var settings = this;
17787 ['xs','sm','md','lg'].map(function(size){
17788 if (settings[size]) {
17789 cfg.cls += ' col-' + size + '-' + settings[size];
17799 initIOSView : function()
17801 this.store.on('load', this.onIOSViewLoad, this);
17806 onIOSViewLoad : function()
17808 if(this.store.getCount() < 1){
17812 this.clearIOSView();
17814 if(this.allowBlank) {
17816 var default_text = '-- SELECT --';
17818 if(this.placeholder.length){
17819 default_text = this.placeholder;
17822 if(this.emptyTitle.length){
17823 default_text += ' - ' + this.emptyTitle + ' -';
17826 var opt = this.inputEl().createChild({
17829 html : default_text
17833 o[this.valueField] = 0;
17834 o[this.displayField] = default_text;
17836 this.ios_options.push({
17843 this.store.data.each(function(d, rowIndex){
17847 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17848 html = d.data[this.displayField];
17853 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17854 value = d.data[this.valueField];
17863 if(this.value == d.data[this.valueField]){
17864 option['selected'] = true;
17867 var opt = this.inputEl().createChild(option);
17869 this.ios_options.push({
17876 this.inputEl().on('change', function(){
17877 this.fireEvent('select', this);
17882 clearIOSView: function()
17884 this.inputEl().dom.innerHTML = '';
17886 this.ios_options = [];
17889 setIOSValue: function(v)
17893 if(!this.ios_options){
17897 Roo.each(this.ios_options, function(opts){
17899 opts.el.dom.removeAttribute('selected');
17901 if(opts.data[this.valueField] != v){
17905 opts.el.dom.setAttribute('selected', true);
17911 * @cfg {Boolean} grow
17915 * @cfg {Number} growMin
17919 * @cfg {Number} growMax
17928 Roo.apply(Roo.bootstrap.ComboBox, {
17932 cls: 'modal-header',
17954 cls: 'list-group-item',
17958 cls: 'roo-combobox-list-group-item-value'
17962 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17976 listItemCheckbox : {
17978 cls: 'list-group-item',
17982 cls: 'roo-combobox-list-group-item-value'
17986 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18002 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18007 cls: 'modal-footer',
18015 cls: 'col-xs-6 text-left',
18018 cls: 'btn btn-danger roo-touch-view-cancel',
18024 cls: 'col-xs-6 text-right',
18027 cls: 'btn btn-success roo-touch-view-ok',
18038 Roo.apply(Roo.bootstrap.ComboBox, {
18040 touchViewTemplate : {
18042 cls: 'modal fade roo-combobox-touch-view',
18046 cls: 'modal-dialog',
18047 style : 'position:fixed', // we have to fix position....
18051 cls: 'modal-content',
18053 Roo.bootstrap.ComboBox.header,
18054 Roo.bootstrap.ComboBox.body,
18055 Roo.bootstrap.ComboBox.footer
18064 * Ext JS Library 1.1.1
18065 * Copyright(c) 2006-2007, Ext JS, LLC.
18067 * Originally Released Under LGPL - original licence link has changed is not relivant.
18070 * <script type="text/javascript">
18075 * @extends Roo.util.Observable
18076 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18077 * This class also supports single and multi selection modes. <br>
18078 * Create a data model bound view:
18080 var store = new Roo.data.Store(...);
18082 var view = new Roo.View({
18084 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18086 singleSelect: true,
18087 selectedClass: "ydataview-selected",
18091 // listen for node click?
18092 view.on("click", function(vw, index, node, e){
18093 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18097 dataModel.load("foobar.xml");
18099 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18101 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18102 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18104 * Note: old style constructor is still suported (container, template, config)
18107 * Create a new View
18108 * @param {Object} config The config object
18111 Roo.View = function(config, depreciated_tpl, depreciated_config){
18113 this.parent = false;
18115 if (typeof(depreciated_tpl) == 'undefined') {
18116 // new way.. - universal constructor.
18117 Roo.apply(this, config);
18118 this.el = Roo.get(this.el);
18121 this.el = Roo.get(config);
18122 this.tpl = depreciated_tpl;
18123 Roo.apply(this, depreciated_config);
18125 this.wrapEl = this.el.wrap().wrap();
18126 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18129 if(typeof(this.tpl) == "string"){
18130 this.tpl = new Roo.Template(this.tpl);
18132 // support xtype ctors..
18133 this.tpl = new Roo.factory(this.tpl, Roo);
18137 this.tpl.compile();
18142 * @event beforeclick
18143 * Fires before a click is processed. Returns false to cancel the default action.
18144 * @param {Roo.View} this
18145 * @param {Number} index The index of the target node
18146 * @param {HTMLElement} node The target node
18147 * @param {Roo.EventObject} e The raw event object
18149 "beforeclick" : true,
18152 * Fires when a template node is clicked.
18153 * @param {Roo.View} this
18154 * @param {Number} index The index of the target node
18155 * @param {HTMLElement} node The target node
18156 * @param {Roo.EventObject} e The raw event object
18161 * Fires when a template node is double clicked.
18162 * @param {Roo.View} this
18163 * @param {Number} index The index of the target node
18164 * @param {HTMLElement} node The target node
18165 * @param {Roo.EventObject} e The raw event object
18169 * @event contextmenu
18170 * Fires when a template node is right clicked.
18171 * @param {Roo.View} this
18172 * @param {Number} index The index of the target node
18173 * @param {HTMLElement} node The target node
18174 * @param {Roo.EventObject} e The raw event object
18176 "contextmenu" : true,
18178 * @event selectionchange
18179 * Fires when the selected nodes change.
18180 * @param {Roo.View} this
18181 * @param {Array} selections Array of the selected nodes
18183 "selectionchange" : true,
18186 * @event beforeselect
18187 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18188 * @param {Roo.View} this
18189 * @param {HTMLElement} node The node to be selected
18190 * @param {Array} selections Array of currently selected nodes
18192 "beforeselect" : true,
18194 * @event preparedata
18195 * Fires on every row to render, to allow you to change the data.
18196 * @param {Roo.View} this
18197 * @param {Object} data to be rendered (change this)
18199 "preparedata" : true
18207 "click": this.onClick,
18208 "dblclick": this.onDblClick,
18209 "contextmenu": this.onContextMenu,
18213 this.selections = [];
18215 this.cmp = new Roo.CompositeElementLite([]);
18217 this.store = Roo.factory(this.store, Roo.data);
18218 this.setStore(this.store, true);
18221 if ( this.footer && this.footer.xtype) {
18223 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18225 this.footer.dataSource = this.store;
18226 this.footer.container = fctr;
18227 this.footer = Roo.factory(this.footer, Roo);
18228 fctr.insertFirst(this.el);
18230 // this is a bit insane - as the paging toolbar seems to detach the el..
18231 // dom.parentNode.parentNode.parentNode
18232 // they get detached?
18236 Roo.View.superclass.constructor.call(this);
18241 Roo.extend(Roo.View, Roo.util.Observable, {
18244 * @cfg {Roo.data.Store} store Data store to load data from.
18249 * @cfg {String|Roo.Element} el The container element.
18254 * @cfg {String|Roo.Template} tpl The template used by this View
18258 * @cfg {String} dataName the named area of the template to use as the data area
18259 * Works with domtemplates roo-name="name"
18263 * @cfg {String} selectedClass The css class to add to selected nodes
18265 selectedClass : "x-view-selected",
18267 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18272 * @cfg {String} text to display on mask (default Loading)
18276 * @cfg {Boolean} multiSelect Allow multiple selection
18278 multiSelect : false,
18280 * @cfg {Boolean} singleSelect Allow single selection
18282 singleSelect: false,
18285 * @cfg {Boolean} toggleSelect - selecting
18287 toggleSelect : false,
18290 * @cfg {Boolean} tickable - selecting
18295 * Returns the element this view is bound to.
18296 * @return {Roo.Element}
18298 getEl : function(){
18299 return this.wrapEl;
18305 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18307 refresh : function(){
18308 //Roo.log('refresh');
18311 // if we are using something like 'domtemplate', then
18312 // the what gets used is:
18313 // t.applySubtemplate(NAME, data, wrapping data..)
18314 // the outer template then get' applied with
18315 // the store 'extra data'
18316 // and the body get's added to the
18317 // roo-name="data" node?
18318 // <span class='roo-tpl-{name}'></span> ?????
18322 this.clearSelections();
18323 this.el.update("");
18325 var records = this.store.getRange();
18326 if(records.length < 1) {
18328 // is this valid?? = should it render a template??
18330 this.el.update(this.emptyText);
18334 if (this.dataName) {
18335 this.el.update(t.apply(this.store.meta)); //????
18336 el = this.el.child('.roo-tpl-' + this.dataName);
18339 for(var i = 0, len = records.length; i < len; i++){
18340 var data = this.prepareData(records[i].data, i, records[i]);
18341 this.fireEvent("preparedata", this, data, i, records[i]);
18343 var d = Roo.apply({}, data);
18346 Roo.apply(d, {'roo-id' : Roo.id()});
18350 Roo.each(this.parent.item, function(item){
18351 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18354 Roo.apply(d, {'roo-data-checked' : 'checked'});
18358 html[html.length] = Roo.util.Format.trim(
18360 t.applySubtemplate(this.dataName, d, this.store.meta) :
18367 el.update(html.join(""));
18368 this.nodes = el.dom.childNodes;
18369 this.updateIndexes(0);
18374 * Function to override to reformat the data that is sent to
18375 * the template for each node.
18376 * DEPRICATED - use the preparedata event handler.
18377 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18378 * a JSON object for an UpdateManager bound view).
18380 prepareData : function(data, index, record)
18382 this.fireEvent("preparedata", this, data, index, record);
18386 onUpdate : function(ds, record){
18387 // Roo.log('on update');
18388 this.clearSelections();
18389 var index = this.store.indexOf(record);
18390 var n = this.nodes[index];
18391 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18392 n.parentNode.removeChild(n);
18393 this.updateIndexes(index, index);
18399 onAdd : function(ds, records, index)
18401 //Roo.log(['on Add', ds, records, index] );
18402 this.clearSelections();
18403 if(this.nodes.length == 0){
18407 var n = this.nodes[index];
18408 for(var i = 0, len = records.length; i < len; i++){
18409 var d = this.prepareData(records[i].data, i, records[i]);
18411 this.tpl.insertBefore(n, d);
18414 this.tpl.append(this.el, d);
18417 this.updateIndexes(index);
18420 onRemove : function(ds, record, index){
18421 // Roo.log('onRemove');
18422 this.clearSelections();
18423 var el = this.dataName ?
18424 this.el.child('.roo-tpl-' + this.dataName) :
18427 el.dom.removeChild(this.nodes[index]);
18428 this.updateIndexes(index);
18432 * Refresh an individual node.
18433 * @param {Number} index
18435 refreshNode : function(index){
18436 this.onUpdate(this.store, this.store.getAt(index));
18439 updateIndexes : function(startIndex, endIndex){
18440 var ns = this.nodes;
18441 startIndex = startIndex || 0;
18442 endIndex = endIndex || ns.length - 1;
18443 for(var i = startIndex; i <= endIndex; i++){
18444 ns[i].nodeIndex = i;
18449 * Changes the data store this view uses and refresh the view.
18450 * @param {Store} store
18452 setStore : function(store, initial){
18453 if(!initial && this.store){
18454 this.store.un("datachanged", this.refresh);
18455 this.store.un("add", this.onAdd);
18456 this.store.un("remove", this.onRemove);
18457 this.store.un("update", this.onUpdate);
18458 this.store.un("clear", this.refresh);
18459 this.store.un("beforeload", this.onBeforeLoad);
18460 this.store.un("load", this.onLoad);
18461 this.store.un("loadexception", this.onLoad);
18465 store.on("datachanged", this.refresh, this);
18466 store.on("add", this.onAdd, this);
18467 store.on("remove", this.onRemove, this);
18468 store.on("update", this.onUpdate, this);
18469 store.on("clear", this.refresh, this);
18470 store.on("beforeload", this.onBeforeLoad, this);
18471 store.on("load", this.onLoad, this);
18472 store.on("loadexception", this.onLoad, this);
18480 * onbeforeLoad - masks the loading area.
18483 onBeforeLoad : function(store,opts)
18485 //Roo.log('onBeforeLoad');
18487 this.el.update("");
18489 this.el.mask(this.mask ? this.mask : "Loading" );
18491 onLoad : function ()
18498 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18499 * @param {HTMLElement} node
18500 * @return {HTMLElement} The template node
18502 findItemFromChild : function(node){
18503 var el = this.dataName ?
18504 this.el.child('.roo-tpl-' + this.dataName,true) :
18507 if(!node || node.parentNode == el){
18510 var p = node.parentNode;
18511 while(p && p != el){
18512 if(p.parentNode == el){
18521 onClick : function(e){
18522 var item = this.findItemFromChild(e.getTarget());
18524 var index = this.indexOf(item);
18525 if(this.onItemClick(item, index, e) !== false){
18526 this.fireEvent("click", this, index, item, e);
18529 this.clearSelections();
18534 onContextMenu : function(e){
18535 var item = this.findItemFromChild(e.getTarget());
18537 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18542 onDblClick : function(e){
18543 var item = this.findItemFromChild(e.getTarget());
18545 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18549 onItemClick : function(item, index, e)
18551 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18554 if (this.toggleSelect) {
18555 var m = this.isSelected(item) ? 'unselect' : 'select';
18558 _t[m](item, true, false);
18561 if(this.multiSelect || this.singleSelect){
18562 if(this.multiSelect && e.shiftKey && this.lastSelection){
18563 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18565 this.select(item, this.multiSelect && e.ctrlKey);
18566 this.lastSelection = item;
18569 if(!this.tickable){
18570 e.preventDefault();
18578 * Get the number of selected nodes.
18581 getSelectionCount : function(){
18582 return this.selections.length;
18586 * Get the currently selected nodes.
18587 * @return {Array} An array of HTMLElements
18589 getSelectedNodes : function(){
18590 return this.selections;
18594 * Get the indexes of the selected nodes.
18597 getSelectedIndexes : function(){
18598 var indexes = [], s = this.selections;
18599 for(var i = 0, len = s.length; i < len; i++){
18600 indexes.push(s[i].nodeIndex);
18606 * Clear all selections
18607 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18609 clearSelections : function(suppressEvent){
18610 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18611 this.cmp.elements = this.selections;
18612 this.cmp.removeClass(this.selectedClass);
18613 this.selections = [];
18614 if(!suppressEvent){
18615 this.fireEvent("selectionchange", this, this.selections);
18621 * Returns true if the passed node is selected
18622 * @param {HTMLElement/Number} node The node or node index
18623 * @return {Boolean}
18625 isSelected : function(node){
18626 var s = this.selections;
18630 node = this.getNode(node);
18631 return s.indexOf(node) !== -1;
18636 * @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
18637 * @param {Boolean} keepExisting (optional) true to keep existing selections
18638 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18640 select : function(nodeInfo, keepExisting, suppressEvent){
18641 if(nodeInfo instanceof Array){
18643 this.clearSelections(true);
18645 for(var i = 0, len = nodeInfo.length; i < len; i++){
18646 this.select(nodeInfo[i], true, true);
18650 var node = this.getNode(nodeInfo);
18651 if(!node || this.isSelected(node)){
18652 return; // already selected.
18655 this.clearSelections(true);
18658 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18659 Roo.fly(node).addClass(this.selectedClass);
18660 this.selections.push(node);
18661 if(!suppressEvent){
18662 this.fireEvent("selectionchange", this, this.selections);
18670 * @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
18671 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18672 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18674 unselect : function(nodeInfo, keepExisting, suppressEvent)
18676 if(nodeInfo instanceof Array){
18677 Roo.each(this.selections, function(s) {
18678 this.unselect(s, nodeInfo);
18682 var node = this.getNode(nodeInfo);
18683 if(!node || !this.isSelected(node)){
18684 //Roo.log("not selected");
18685 return; // not selected.
18689 Roo.each(this.selections, function(s) {
18691 Roo.fly(node).removeClass(this.selectedClass);
18698 this.selections= ns;
18699 this.fireEvent("selectionchange", this, this.selections);
18703 * Gets a template node.
18704 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18705 * @return {HTMLElement} The node or null if it wasn't found
18707 getNode : function(nodeInfo){
18708 if(typeof nodeInfo == "string"){
18709 return document.getElementById(nodeInfo);
18710 }else if(typeof nodeInfo == "number"){
18711 return this.nodes[nodeInfo];
18717 * Gets a range template nodes.
18718 * @param {Number} startIndex
18719 * @param {Number} endIndex
18720 * @return {Array} An array of nodes
18722 getNodes : function(start, end){
18723 var ns = this.nodes;
18724 start = start || 0;
18725 end = typeof end == "undefined" ? ns.length - 1 : end;
18728 for(var i = start; i <= end; i++){
18732 for(var i = start; i >= end; i--){
18740 * Finds the index of the passed node
18741 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18742 * @return {Number} The index of the node or -1
18744 indexOf : function(node){
18745 node = this.getNode(node);
18746 if(typeof node.nodeIndex == "number"){
18747 return node.nodeIndex;
18749 var ns = this.nodes;
18750 for(var i = 0, len = ns.length; i < len; i++){
18761 * based on jquery fullcalendar
18765 Roo.bootstrap = Roo.bootstrap || {};
18767 * @class Roo.bootstrap.Calendar
18768 * @extends Roo.bootstrap.Component
18769 * Bootstrap Calendar class
18770 * @cfg {Boolean} loadMask (true|false) default false
18771 * @cfg {Object} header generate the user specific header of the calendar, default false
18774 * Create a new Container
18775 * @param {Object} config The config object
18780 Roo.bootstrap.Calendar = function(config){
18781 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18785 * Fires when a date is selected
18786 * @param {DatePicker} this
18787 * @param {Date} date The selected date
18791 * @event monthchange
18792 * Fires when the displayed month changes
18793 * @param {DatePicker} this
18794 * @param {Date} date The selected month
18796 'monthchange': true,
18798 * @event evententer
18799 * Fires when mouse over an event
18800 * @param {Calendar} this
18801 * @param {event} Event
18803 'evententer': true,
18805 * @event eventleave
18806 * Fires when the mouse leaves an
18807 * @param {Calendar} this
18810 'eventleave': true,
18812 * @event eventclick
18813 * Fires when the mouse click an
18814 * @param {Calendar} this
18823 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18826 * @cfg {Number} startDay
18827 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18835 getAutoCreate : function(){
18838 var fc_button = function(name, corner, style, content ) {
18839 return Roo.apply({},{
18841 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18843 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18846 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18857 style : 'width:100%',
18864 cls : 'fc-header-left',
18866 fc_button('prev', 'left', 'arrow', '‹' ),
18867 fc_button('next', 'right', 'arrow', '›' ),
18868 { tag: 'span', cls: 'fc-header-space' },
18869 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18877 cls : 'fc-header-center',
18881 cls: 'fc-header-title',
18884 html : 'month / year'
18892 cls : 'fc-header-right',
18894 /* fc_button('month', 'left', '', 'month' ),
18895 fc_button('week', '', '', 'week' ),
18896 fc_button('day', 'right', '', 'day' )
18908 header = this.header;
18911 var cal_heads = function() {
18913 // fixme - handle this.
18915 for (var i =0; i < Date.dayNames.length; i++) {
18916 var d = Date.dayNames[i];
18919 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18920 html : d.substring(0,3)
18924 ret[0].cls += ' fc-first';
18925 ret[6].cls += ' fc-last';
18928 var cal_cell = function(n) {
18931 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18936 cls: 'fc-day-number',
18940 cls: 'fc-day-content',
18944 style: 'position: relative;' // height: 17px;
18956 var cal_rows = function() {
18959 for (var r = 0; r < 6; r++) {
18966 for (var i =0; i < Date.dayNames.length; i++) {
18967 var d = Date.dayNames[i];
18968 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18971 row.cn[0].cls+=' fc-first';
18972 row.cn[0].cn[0].style = 'min-height:90px';
18973 row.cn[6].cls+=' fc-last';
18977 ret[0].cls += ' fc-first';
18978 ret[4].cls += ' fc-prev-last';
18979 ret[5].cls += ' fc-last';
18986 cls: 'fc-border-separate',
18987 style : 'width:100%',
18995 cls : 'fc-first fc-last',
19013 cls : 'fc-content',
19014 style : "position: relative;",
19017 cls : 'fc-view fc-view-month fc-grid',
19018 style : 'position: relative',
19019 unselectable : 'on',
19022 cls : 'fc-event-container',
19023 style : 'position:absolute;z-index:8;top:0;left:0;'
19041 initEvents : function()
19044 throw "can not find store for calendar";
19050 style: "text-align:center",
19054 style: "background-color:white;width:50%;margin:250 auto",
19058 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19069 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19071 var size = this.el.select('.fc-content', true).first().getSize();
19072 this.maskEl.setSize(size.width, size.height);
19073 this.maskEl.enableDisplayMode("block");
19074 if(!this.loadMask){
19075 this.maskEl.hide();
19078 this.store = Roo.factory(this.store, Roo.data);
19079 this.store.on('load', this.onLoad, this);
19080 this.store.on('beforeload', this.onBeforeLoad, this);
19084 this.cells = this.el.select('.fc-day',true);
19085 //Roo.log(this.cells);
19086 this.textNodes = this.el.query('.fc-day-number');
19087 this.cells.addClassOnOver('fc-state-hover');
19089 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19090 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19091 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19092 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19094 this.on('monthchange', this.onMonthChange, this);
19096 this.update(new Date().clearTime());
19099 resize : function() {
19100 var sz = this.el.getSize();
19102 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19103 this.el.select('.fc-day-content div',true).setHeight(34);
19108 showPrevMonth : function(e){
19109 this.update(this.activeDate.add("mo", -1));
19111 showToday : function(e){
19112 this.update(new Date().clearTime());
19115 showNextMonth : function(e){
19116 this.update(this.activeDate.add("mo", 1));
19120 showPrevYear : function(){
19121 this.update(this.activeDate.add("y", -1));
19125 showNextYear : function(){
19126 this.update(this.activeDate.add("y", 1));
19131 update : function(date)
19133 var vd = this.activeDate;
19134 this.activeDate = date;
19135 // if(vd && this.el){
19136 // var t = date.getTime();
19137 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19138 // Roo.log('using add remove');
19140 // this.fireEvent('monthchange', this, date);
19142 // this.cells.removeClass("fc-state-highlight");
19143 // this.cells.each(function(c){
19144 // if(c.dateValue == t){
19145 // c.addClass("fc-state-highlight");
19146 // setTimeout(function(){
19147 // try{c.dom.firstChild.focus();}catch(e){}
19157 var days = date.getDaysInMonth();
19159 var firstOfMonth = date.getFirstDateOfMonth();
19160 var startingPos = firstOfMonth.getDay()-this.startDay;
19162 if(startingPos < this.startDay){
19166 var pm = date.add(Date.MONTH, -1);
19167 var prevStart = pm.getDaysInMonth()-startingPos;
19169 this.cells = this.el.select('.fc-day',true);
19170 this.textNodes = this.el.query('.fc-day-number');
19171 this.cells.addClassOnOver('fc-state-hover');
19173 var cells = this.cells.elements;
19174 var textEls = this.textNodes;
19176 Roo.each(cells, function(cell){
19177 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19180 days += startingPos;
19182 // convert everything to numbers so it's fast
19183 var day = 86400000;
19184 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19187 //Roo.log(prevStart);
19189 var today = new Date().clearTime().getTime();
19190 var sel = date.clearTime().getTime();
19191 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19192 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19193 var ddMatch = this.disabledDatesRE;
19194 var ddText = this.disabledDatesText;
19195 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19196 var ddaysText = this.disabledDaysText;
19197 var format = this.format;
19199 var setCellClass = function(cal, cell){
19203 //Roo.log('set Cell Class');
19205 var t = d.getTime();
19209 cell.dateValue = t;
19211 cell.className += " fc-today";
19212 cell.className += " fc-state-highlight";
19213 cell.title = cal.todayText;
19216 // disable highlight in other month..
19217 //cell.className += " fc-state-highlight";
19222 cell.className = " fc-state-disabled";
19223 cell.title = cal.minText;
19227 cell.className = " fc-state-disabled";
19228 cell.title = cal.maxText;
19232 if(ddays.indexOf(d.getDay()) != -1){
19233 cell.title = ddaysText;
19234 cell.className = " fc-state-disabled";
19237 if(ddMatch && format){
19238 var fvalue = d.dateFormat(format);
19239 if(ddMatch.test(fvalue)){
19240 cell.title = ddText.replace("%0", fvalue);
19241 cell.className = " fc-state-disabled";
19245 if (!cell.initialClassName) {
19246 cell.initialClassName = cell.dom.className;
19249 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19254 for(; i < startingPos; i++) {
19255 textEls[i].innerHTML = (++prevStart);
19256 d.setDate(d.getDate()+1);
19258 cells[i].className = "fc-past fc-other-month";
19259 setCellClass(this, cells[i]);
19264 for(; i < days; i++){
19265 intDay = i - startingPos + 1;
19266 textEls[i].innerHTML = (intDay);
19267 d.setDate(d.getDate()+1);
19269 cells[i].className = ''; // "x-date-active";
19270 setCellClass(this, cells[i]);
19274 for(; i < 42; i++) {
19275 textEls[i].innerHTML = (++extraDays);
19276 d.setDate(d.getDate()+1);
19278 cells[i].className = "fc-future fc-other-month";
19279 setCellClass(this, cells[i]);
19282 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19284 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19286 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19287 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19289 if(totalRows != 6){
19290 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19291 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19294 this.fireEvent('monthchange', this, date);
19298 if(!this.internalRender){
19299 var main = this.el.dom.firstChild;
19300 var w = main.offsetWidth;
19301 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19302 Roo.fly(main).setWidth(w);
19303 this.internalRender = true;
19304 // opera does not respect the auto grow header center column
19305 // then, after it gets a width opera refuses to recalculate
19306 // without a second pass
19307 if(Roo.isOpera && !this.secondPass){
19308 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19309 this.secondPass = true;
19310 this.update.defer(10, this, [date]);
19317 findCell : function(dt) {
19318 dt = dt.clearTime().getTime();
19320 this.cells.each(function(c){
19321 //Roo.log("check " +c.dateValue + '?=' + dt);
19322 if(c.dateValue == dt){
19332 findCells : function(ev) {
19333 var s = ev.start.clone().clearTime().getTime();
19335 var e= ev.end.clone().clearTime().getTime();
19338 this.cells.each(function(c){
19339 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19341 if(c.dateValue > e){
19344 if(c.dateValue < s){
19353 // findBestRow: function(cells)
19357 // for (var i =0 ; i < cells.length;i++) {
19358 // ret = Math.max(cells[i].rows || 0,ret);
19365 addItem : function(ev)
19367 // look for vertical location slot in
19368 var cells = this.findCells(ev);
19370 // ev.row = this.findBestRow(cells);
19372 // work out the location.
19376 for(var i =0; i < cells.length; i++) {
19378 cells[i].row = cells[0].row;
19381 cells[i].row = cells[i].row + 1;
19391 if (crow.start.getY() == cells[i].getY()) {
19393 crow.end = cells[i];
19410 cells[0].events.push(ev);
19412 this.calevents.push(ev);
19415 clearEvents: function() {
19417 if(!this.calevents){
19421 Roo.each(this.cells.elements, function(c){
19427 Roo.each(this.calevents, function(e) {
19428 Roo.each(e.els, function(el) {
19429 el.un('mouseenter' ,this.onEventEnter, this);
19430 el.un('mouseleave' ,this.onEventLeave, this);
19435 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19441 renderEvents: function()
19445 this.cells.each(function(c) {
19454 if(c.row != c.events.length){
19455 r = 4 - (4 - (c.row - c.events.length));
19458 c.events = ev.slice(0, r);
19459 c.more = ev.slice(r);
19461 if(c.more.length && c.more.length == 1){
19462 c.events.push(c.more.pop());
19465 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19469 this.cells.each(function(c) {
19471 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19474 for (var e = 0; e < c.events.length; e++){
19475 var ev = c.events[e];
19476 var rows = ev.rows;
19478 for(var i = 0; i < rows.length; i++) {
19480 // how many rows should it span..
19483 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19484 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19486 unselectable : "on",
19489 cls: 'fc-event-inner',
19493 // cls: 'fc-event-time',
19494 // html : cells.length > 1 ? '' : ev.time
19498 cls: 'fc-event-title',
19499 html : String.format('{0}', ev.title)
19506 cls: 'ui-resizable-handle ui-resizable-e',
19507 html : '  '
19514 cfg.cls += ' fc-event-start';
19516 if ((i+1) == rows.length) {
19517 cfg.cls += ' fc-event-end';
19520 var ctr = _this.el.select('.fc-event-container',true).first();
19521 var cg = ctr.createChild(cfg);
19523 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19524 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19526 var r = (c.more.length) ? 1 : 0;
19527 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19528 cg.setWidth(ebox.right - sbox.x -2);
19530 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19531 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19532 cg.on('click', _this.onEventClick, _this, ev);
19543 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19544 style : 'position: absolute',
19545 unselectable : "on",
19548 cls: 'fc-event-inner',
19552 cls: 'fc-event-title',
19560 cls: 'ui-resizable-handle ui-resizable-e',
19561 html : '  '
19567 var ctr = _this.el.select('.fc-event-container',true).first();
19568 var cg = ctr.createChild(cfg);
19570 var sbox = c.select('.fc-day-content',true).first().getBox();
19571 var ebox = c.select('.fc-day-content',true).first().getBox();
19573 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19574 cg.setWidth(ebox.right - sbox.x -2);
19576 cg.on('click', _this.onMoreEventClick, _this, c.more);
19586 onEventEnter: function (e, el,event,d) {
19587 this.fireEvent('evententer', this, el, event);
19590 onEventLeave: function (e, el,event,d) {
19591 this.fireEvent('eventleave', this, el, event);
19594 onEventClick: function (e, el,event,d) {
19595 this.fireEvent('eventclick', this, el, event);
19598 onMonthChange: function () {
19602 onMoreEventClick: function(e, el, more)
19606 this.calpopover.placement = 'right';
19607 this.calpopover.setTitle('More');
19609 this.calpopover.setContent('');
19611 var ctr = this.calpopover.el.select('.popover-content', true).first();
19613 Roo.each(more, function(m){
19615 cls : 'fc-event-hori fc-event-draggable',
19618 var cg = ctr.createChild(cfg);
19620 cg.on('click', _this.onEventClick, _this, m);
19623 this.calpopover.show(el);
19628 onLoad: function ()
19630 this.calevents = [];
19633 if(this.store.getCount() > 0){
19634 this.store.data.each(function(d){
19637 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19638 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19639 time : d.data.start_time,
19640 title : d.data.title,
19641 description : d.data.description,
19642 venue : d.data.venue
19647 this.renderEvents();
19649 if(this.calevents.length && this.loadMask){
19650 this.maskEl.hide();
19654 onBeforeLoad: function()
19656 this.clearEvents();
19658 this.maskEl.show();
19672 * @class Roo.bootstrap.Popover
19673 * @extends Roo.bootstrap.Component
19674 * Bootstrap Popover class
19675 * @cfg {String} html contents of the popover (or false to use children..)
19676 * @cfg {String} title of popover (or false to hide)
19677 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19678 * @cfg {String} trigger click || hover (or false to trigger manually)
19679 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19680 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19681 * - if false and it has a 'parent' then it will be automatically added to that element
19682 * - if string - Roo.get will be called
19683 * @cfg {Number} delay - delay before showing
19686 * Create a new Popover
19687 * @param {Object} config The config object
19690 Roo.bootstrap.Popover = function(config){
19691 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19697 * After the popover show
19699 * @param {Roo.bootstrap.Popover} this
19704 * After the popover hide
19706 * @param {Roo.bootstrap.Popover} this
19712 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19717 placement : 'right',
19718 trigger : 'hover', // hover
19724 can_build_overlaid : false,
19726 maskEl : false, // the mask element
19729 alignEl : false, // when show is called with an element - this get's stored.
19731 getChildContainer : function()
19733 return this.contentEl;
19736 getPopoverHeader : function()
19738 this.title = true; // flag not to hide it..
19739 this.headerEl.addClass('p-0');
19740 return this.headerEl
19744 getAutoCreate : function(){
19747 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19748 style: 'display:block',
19754 cls : 'popover-inner ',
19758 cls: 'popover-title popover-header',
19759 html : this.title === false ? '' : this.title
19762 cls : 'popover-content popover-body ' + (this.cls || ''),
19763 html : this.html || ''
19774 * @param {string} the title
19776 setTitle: function(str)
19780 this.headerEl.dom.innerHTML = str;
19785 * @param {string} the body content
19787 setContent: function(str)
19790 if (this.contentEl) {
19791 this.contentEl.dom.innerHTML = str;
19795 // as it get's added to the bottom of the page.
19796 onRender : function(ct, position)
19798 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19803 var cfg = Roo.apply({}, this.getAutoCreate());
19807 cfg.cls += ' ' + this.cls;
19810 cfg.style = this.style;
19812 //Roo.log("adding to ");
19813 this.el = Roo.get(document.body).createChild(cfg, position);
19814 // Roo.log(this.el);
19817 this.contentEl = this.el.select('.popover-content',true).first();
19818 this.headerEl = this.el.select('.popover-title',true).first();
19821 if(typeof(this.items) != 'undefined'){
19822 var items = this.items;
19825 for(var i =0;i < items.length;i++) {
19826 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19830 this.items = nitems;
19832 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19833 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19840 resizeMask : function()
19842 this.maskEl.setSize(
19843 Roo.lib.Dom.getViewWidth(true),
19844 Roo.lib.Dom.getViewHeight(true)
19848 initEvents : function()
19852 Roo.bootstrap.Popover.register(this);
19855 this.arrowEl = this.el.select('.arrow',true).first();
19856 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19857 this.el.enableDisplayMode('block');
19861 if (this.over === false && !this.parent()) {
19864 if (this.triggers === false) {
19869 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19870 var triggers = this.trigger ? this.trigger.split(' ') : [];
19871 Roo.each(triggers, function(trigger) {
19873 if (trigger == 'click') {
19874 on_el.on('click', this.toggle, this);
19875 } else if (trigger != 'manual') {
19876 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19877 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19879 on_el.on(eventIn ,this.enter, this);
19880 on_el.on(eventOut, this.leave, this);
19890 toggle : function () {
19891 this.hoverState == 'in' ? this.leave() : this.enter();
19894 enter : function () {
19896 clearTimeout(this.timeout);
19898 this.hoverState = 'in';
19900 if (!this.delay || !this.delay.show) {
19905 this.timeout = setTimeout(function () {
19906 if (_t.hoverState == 'in') {
19909 }, this.delay.show)
19912 leave : function() {
19913 clearTimeout(this.timeout);
19915 this.hoverState = 'out';
19917 if (!this.delay || !this.delay.hide) {
19922 this.timeout = setTimeout(function () {
19923 if (_t.hoverState == 'out') {
19926 }, this.delay.hide)
19930 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19931 * @param {string} (left|right|top|bottom) position
19933 show : function (on_el, placement)
19935 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19936 on_el = on_el || false; // default to false
19939 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19940 on_el = this.parent().el;
19941 } else if (this.over) {
19942 Roo.get(this.over);
19947 this.alignEl = Roo.get( on_el );
19950 this.render(document.body);
19956 if (this.title === false) {
19957 this.headerEl.hide();
19962 this.el.dom.style.display = 'block';
19965 if (this.alignEl) {
19966 this.updatePosition(this.placement, true);
19969 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19970 var es = this.el.getSize();
19971 var x = Roo.lib.Dom.getViewWidth()/2;
19972 var y = Roo.lib.Dom.getViewHeight()/2;
19973 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19978 //var arrow = this.el.select('.arrow',true).first();
19979 //arrow.set(align[2],
19981 this.el.addClass('in');
19985 this.hoverState = 'in';
19988 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19989 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19990 this.maskEl.dom.style.display = 'block';
19991 this.maskEl.addClass('show');
19993 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19995 this.fireEvent('show', this);
19999 * fire this manually after loading a grid in the table for example
20000 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20001 * @param {Boolean} try and move it if we cant get right position.
20003 updatePosition : function(placement, try_move)
20005 // allow for calling with no parameters
20006 placement = placement ? placement : this.placement;
20007 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20009 this.el.removeClass([
20010 'fade','top','bottom', 'left', 'right','in',
20011 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20013 this.el.addClass(placement + ' bs-popover-' + placement);
20015 if (!this.alignEl ) {
20019 switch (placement) {
20021 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20022 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20023 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20024 //normal display... or moved up/down.
20025 this.el.setXY(offset);
20026 var xy = this.alignEl.getAnchorXY('tr', false);
20028 this.arrowEl.setXY(xy);
20031 // continue through...
20032 return this.updatePosition('left', false);
20036 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20037 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20038 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20039 //normal display... or moved up/down.
20040 this.el.setXY(offset);
20041 var xy = this.alignEl.getAnchorXY('tl', false);
20042 xy[0]-=10;xy[1]+=5; // << fix me
20043 this.arrowEl.setXY(xy);
20047 return this.updatePosition('right', false);
20050 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20051 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20052 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20053 //normal display... or moved up/down.
20054 this.el.setXY(offset);
20055 var xy = this.alignEl.getAnchorXY('t', false);
20056 xy[1]-=10; // << fix me
20057 this.arrowEl.setXY(xy);
20061 return this.updatePosition('bottom', false);
20064 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20065 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20066 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20067 //normal display... or moved up/down.
20068 this.el.setXY(offset);
20069 var xy = this.alignEl.getAnchorXY('b', false);
20070 xy[1]+=2; // << fix me
20071 this.arrowEl.setXY(xy);
20075 return this.updatePosition('top', false);
20086 this.el.setXY([0,0]);
20087 this.el.removeClass('in');
20089 this.hoverState = null;
20090 this.maskEl.hide(); // always..
20091 this.fireEvent('hide', this);
20097 Roo.apply(Roo.bootstrap.Popover, {
20100 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20101 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20102 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20103 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20108 clickHander : false,
20111 onMouseDown : function(e)
20113 if (!e.getTarget(".roo-popover")) {
20121 register : function(popup)
20123 if (!Roo.bootstrap.Popover.clickHandler) {
20124 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20126 // hide other popups.
20128 this.popups.push(popup);
20130 hideAll : function()
20132 this.popups.forEach(function(p) {
20140 * Card header - holder for the card header elements.
20145 * @class Roo.bootstrap.PopoverNav
20146 * @extends Roo.bootstrap.NavGroup
20147 * Bootstrap Popover header navigation class
20149 * Create a new Popover Header Navigation
20150 * @param {Object} config The config object
20153 Roo.bootstrap.PopoverNav = function(config){
20154 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20157 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20160 container_method : 'getPopoverHeader'
20178 * @class Roo.bootstrap.Progress
20179 * @extends Roo.bootstrap.Component
20180 * Bootstrap Progress class
20181 * @cfg {Boolean} striped striped of the progress bar
20182 * @cfg {Boolean} active animated of the progress bar
20186 * Create a new Progress
20187 * @param {Object} config The config object
20190 Roo.bootstrap.Progress = function(config){
20191 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20194 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20199 getAutoCreate : function(){
20207 cfg.cls += ' progress-striped';
20211 cfg.cls += ' active';
20230 * @class Roo.bootstrap.ProgressBar
20231 * @extends Roo.bootstrap.Component
20232 * Bootstrap ProgressBar class
20233 * @cfg {Number} aria_valuenow aria-value now
20234 * @cfg {Number} aria_valuemin aria-value min
20235 * @cfg {Number} aria_valuemax aria-value max
20236 * @cfg {String} label label for the progress bar
20237 * @cfg {String} panel (success | info | warning | danger )
20238 * @cfg {String} role role of the progress bar
20239 * @cfg {String} sr_only text
20243 * Create a new ProgressBar
20244 * @param {Object} config The config object
20247 Roo.bootstrap.ProgressBar = function(config){
20248 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20251 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20255 aria_valuemax : 100,
20261 getAutoCreate : function()
20266 cls: 'progress-bar',
20267 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20279 cfg.role = this.role;
20282 if(this.aria_valuenow){
20283 cfg['aria-valuenow'] = this.aria_valuenow;
20286 if(this.aria_valuemin){
20287 cfg['aria-valuemin'] = this.aria_valuemin;
20290 if(this.aria_valuemax){
20291 cfg['aria-valuemax'] = this.aria_valuemax;
20294 if(this.label && !this.sr_only){
20295 cfg.html = this.label;
20299 cfg.cls += ' progress-bar-' + this.panel;
20305 update : function(aria_valuenow)
20307 this.aria_valuenow = aria_valuenow;
20309 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20324 * @class Roo.bootstrap.TabGroup
20325 * @extends Roo.bootstrap.Column
20326 * Bootstrap Column class
20327 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20328 * @cfg {Boolean} carousel true to make the group behave like a carousel
20329 * @cfg {Boolean} bullets show bullets for the panels
20330 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20331 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20332 * @cfg {Boolean} showarrow (true|false) show arrow default true
20335 * Create a new TabGroup
20336 * @param {Object} config The config object
20339 Roo.bootstrap.TabGroup = function(config){
20340 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20342 this.navId = Roo.id();
20345 Roo.bootstrap.TabGroup.register(this);
20349 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20352 transition : false,
20357 slideOnTouch : false,
20360 getAutoCreate : function()
20362 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20364 cfg.cls += ' tab-content';
20366 if (this.carousel) {
20367 cfg.cls += ' carousel slide';
20370 cls : 'carousel-inner',
20374 if(this.bullets && !Roo.isTouch){
20377 cls : 'carousel-bullets',
20381 if(this.bullets_cls){
20382 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20389 cfg.cn[0].cn.push(bullets);
20392 if(this.showarrow){
20393 cfg.cn[0].cn.push({
20395 class : 'carousel-arrow',
20399 class : 'carousel-prev',
20403 class : 'fa fa-chevron-left'
20409 class : 'carousel-next',
20413 class : 'fa fa-chevron-right'
20426 initEvents: function()
20428 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20429 // this.el.on("touchstart", this.onTouchStart, this);
20432 if(this.autoslide){
20435 this.slideFn = window.setInterval(function() {
20436 _this.showPanelNext();
20440 if(this.showarrow){
20441 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20442 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20448 // onTouchStart : function(e, el, o)
20450 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20454 // this.showPanelNext();
20458 getChildContainer : function()
20460 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20464 * register a Navigation item
20465 * @param {Roo.bootstrap.NavItem} the navitem to add
20467 register : function(item)
20469 this.tabs.push( item);
20470 item.navId = this.navId; // not really needed..
20475 getActivePanel : function()
20478 Roo.each(this.tabs, function(t) {
20488 getPanelByName : function(n)
20491 Roo.each(this.tabs, function(t) {
20492 if (t.tabId == n) {
20500 indexOfPanel : function(p)
20503 Roo.each(this.tabs, function(t,i) {
20504 if (t.tabId == p.tabId) {
20513 * show a specific panel
20514 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20515 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20517 showPanel : function (pan)
20519 if(this.transition || typeof(pan) == 'undefined'){
20520 Roo.log("waiting for the transitionend");
20524 if (typeof(pan) == 'number') {
20525 pan = this.tabs[pan];
20528 if (typeof(pan) == 'string') {
20529 pan = this.getPanelByName(pan);
20532 var cur = this.getActivePanel();
20535 Roo.log('pan or acitve pan is undefined');
20539 if (pan.tabId == this.getActivePanel().tabId) {
20543 if (false === cur.fireEvent('beforedeactivate')) {
20547 if(this.bullets > 0 && !Roo.isTouch){
20548 this.setActiveBullet(this.indexOfPanel(pan));
20551 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20553 //class="carousel-item carousel-item-next carousel-item-left"
20555 this.transition = true;
20556 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20557 var lr = dir == 'next' ? 'left' : 'right';
20558 pan.el.addClass(dir); // or prev
20559 pan.el.addClass('carousel-item-' + dir); // or prev
20560 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20561 cur.el.addClass(lr); // or right
20562 pan.el.addClass(lr);
20563 cur.el.addClass('carousel-item-' +lr); // or right
20564 pan.el.addClass('carousel-item-' +lr);
20568 cur.el.on('transitionend', function() {
20569 Roo.log("trans end?");
20571 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20572 pan.setActive(true);
20574 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20575 cur.setActive(false);
20577 _this.transition = false;
20579 }, this, { single: true } );
20584 cur.setActive(false);
20585 pan.setActive(true);
20590 showPanelNext : function()
20592 var i = this.indexOfPanel(this.getActivePanel());
20594 if (i >= this.tabs.length - 1 && !this.autoslide) {
20598 if (i >= this.tabs.length - 1 && this.autoslide) {
20602 this.showPanel(this.tabs[i+1]);
20605 showPanelPrev : function()
20607 var i = this.indexOfPanel(this.getActivePanel());
20609 if (i < 1 && !this.autoslide) {
20613 if (i < 1 && this.autoslide) {
20614 i = this.tabs.length;
20617 this.showPanel(this.tabs[i-1]);
20621 addBullet: function()
20623 if(!this.bullets || Roo.isTouch){
20626 var ctr = this.el.select('.carousel-bullets',true).first();
20627 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20628 var bullet = ctr.createChild({
20629 cls : 'bullet bullet-' + i
20630 },ctr.dom.lastChild);
20635 bullet.on('click', (function(e, el, o, ii, t){
20637 e.preventDefault();
20639 this.showPanel(ii);
20641 if(this.autoslide && this.slideFn){
20642 clearInterval(this.slideFn);
20643 this.slideFn = window.setInterval(function() {
20644 _this.showPanelNext();
20648 }).createDelegate(this, [i, bullet], true));
20653 setActiveBullet : function(i)
20659 Roo.each(this.el.select('.bullet', true).elements, function(el){
20660 el.removeClass('selected');
20663 var bullet = this.el.select('.bullet-' + i, true).first();
20669 bullet.addClass('selected');
20680 Roo.apply(Roo.bootstrap.TabGroup, {
20684 * register a Navigation Group
20685 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20687 register : function(navgrp)
20689 this.groups[navgrp.navId] = navgrp;
20693 * fetch a Navigation Group based on the navigation ID
20694 * if one does not exist , it will get created.
20695 * @param {string} the navgroup to add
20696 * @returns {Roo.bootstrap.NavGroup} the navgroup
20698 get: function(navId) {
20699 if (typeof(this.groups[navId]) == 'undefined') {
20700 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20702 return this.groups[navId] ;
20717 * @class Roo.bootstrap.TabPanel
20718 * @extends Roo.bootstrap.Component
20719 * Bootstrap TabPanel class
20720 * @cfg {Boolean} active panel active
20721 * @cfg {String} html panel content
20722 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20723 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20724 * @cfg {String} href click to link..
20725 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20729 * Create a new TabPanel
20730 * @param {Object} config The config object
20733 Roo.bootstrap.TabPanel = function(config){
20734 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20738 * Fires when the active status changes
20739 * @param {Roo.bootstrap.TabPanel} this
20740 * @param {Boolean} state the new state
20745 * @event beforedeactivate
20746 * Fires before a tab is de-activated - can be used to do validation on a form.
20747 * @param {Roo.bootstrap.TabPanel} this
20748 * @return {Boolean} false if there is an error
20751 'beforedeactivate': true
20754 this.tabId = this.tabId || Roo.id();
20758 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20765 touchSlide : false,
20766 getAutoCreate : function(){
20771 // item is needed for carousel - not sure if it has any effect otherwise
20772 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20773 html: this.html || ''
20777 cfg.cls += ' active';
20781 cfg.tabId = this.tabId;
20789 initEvents: function()
20791 var p = this.parent();
20793 this.navId = this.navId || p.navId;
20795 if (typeof(this.navId) != 'undefined') {
20796 // not really needed.. but just in case.. parent should be a NavGroup.
20797 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20801 var i = tg.tabs.length - 1;
20803 if(this.active && tg.bullets > 0 && i < tg.bullets){
20804 tg.setActiveBullet(i);
20808 this.el.on('click', this.onClick, this);
20810 if(Roo.isTouch && this.touchSlide){
20811 this.el.on("touchstart", this.onTouchStart, this);
20812 this.el.on("touchmove", this.onTouchMove, this);
20813 this.el.on("touchend", this.onTouchEnd, this);
20818 onRender : function(ct, position)
20820 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20823 setActive : function(state)
20825 Roo.log("panel - set active " + this.tabId + "=" + state);
20827 this.active = state;
20829 this.el.removeClass('active');
20831 } else if (!this.el.hasClass('active')) {
20832 this.el.addClass('active');
20835 this.fireEvent('changed', this, state);
20838 onClick : function(e)
20840 e.preventDefault();
20842 if(!this.href.length){
20846 window.location.href = this.href;
20855 onTouchStart : function(e)
20857 this.swiping = false;
20859 this.startX = e.browserEvent.touches[0].clientX;
20860 this.startY = e.browserEvent.touches[0].clientY;
20863 onTouchMove : function(e)
20865 this.swiping = true;
20867 this.endX = e.browserEvent.touches[0].clientX;
20868 this.endY = e.browserEvent.touches[0].clientY;
20871 onTouchEnd : function(e)
20878 var tabGroup = this.parent();
20880 if(this.endX > this.startX){ // swiping right
20881 tabGroup.showPanelPrev();
20885 if(this.startX > this.endX){ // swiping left
20886 tabGroup.showPanelNext();
20905 * @class Roo.bootstrap.DateField
20906 * @extends Roo.bootstrap.Input
20907 * Bootstrap DateField class
20908 * @cfg {Number} weekStart default 0
20909 * @cfg {String} viewMode default empty, (months|years)
20910 * @cfg {String} minViewMode default empty, (months|years)
20911 * @cfg {Number} startDate default -Infinity
20912 * @cfg {Number} endDate default Infinity
20913 * @cfg {Boolean} todayHighlight default false
20914 * @cfg {Boolean} todayBtn default false
20915 * @cfg {Boolean} calendarWeeks default false
20916 * @cfg {Object} daysOfWeekDisabled default empty
20917 * @cfg {Boolean} singleMode default false (true | false)
20919 * @cfg {Boolean} keyboardNavigation default true
20920 * @cfg {String} language default en
20923 * Create a new DateField
20924 * @param {Object} config The config object
20927 Roo.bootstrap.DateField = function(config){
20928 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20932 * Fires when this field show.
20933 * @param {Roo.bootstrap.DateField} this
20934 * @param {Mixed} date The date value
20939 * Fires when this field hide.
20940 * @param {Roo.bootstrap.DateField} this
20941 * @param {Mixed} date The date value
20946 * Fires when select a date.
20947 * @param {Roo.bootstrap.DateField} this
20948 * @param {Mixed} date The date value
20952 * @event beforeselect
20953 * Fires when before select a date.
20954 * @param {Roo.bootstrap.DateField} this
20955 * @param {Mixed} date The date value
20957 beforeselect : true
20961 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20964 * @cfg {String} format
20965 * The default date format string which can be overriden for localization support. The format must be
20966 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20970 * @cfg {String} altFormats
20971 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20972 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20974 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20982 todayHighlight : false,
20988 keyboardNavigation: true,
20990 calendarWeeks: false,
20992 startDate: -Infinity,
20996 daysOfWeekDisabled: [],
21000 singleMode : false,
21002 UTCDate: function()
21004 return new Date(Date.UTC.apply(Date, arguments));
21007 UTCToday: function()
21009 var today = new Date();
21010 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21013 getDate: function() {
21014 var d = this.getUTCDate();
21015 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21018 getUTCDate: function() {
21022 setDate: function(d) {
21023 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21026 setUTCDate: function(d) {
21028 this.setValue(this.formatDate(this.date));
21031 onRender: function(ct, position)
21034 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21036 this.language = this.language || 'en';
21037 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21038 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21040 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21041 this.format = this.format || 'm/d/y';
21042 this.isInline = false;
21043 this.isInput = true;
21044 this.component = this.el.select('.add-on', true).first() || false;
21045 this.component = (this.component && this.component.length === 0) ? false : this.component;
21046 this.hasInput = this.component && this.inputEl().length;
21048 if (typeof(this.minViewMode === 'string')) {
21049 switch (this.minViewMode) {
21051 this.minViewMode = 1;
21054 this.minViewMode = 2;
21057 this.minViewMode = 0;
21062 if (typeof(this.viewMode === 'string')) {
21063 switch (this.viewMode) {
21076 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21078 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21080 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21082 this.picker().on('mousedown', this.onMousedown, this);
21083 this.picker().on('click', this.onClick, this);
21085 this.picker().addClass('datepicker-dropdown');
21087 this.startViewMode = this.viewMode;
21089 if(this.singleMode){
21090 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21091 v.setVisibilityMode(Roo.Element.DISPLAY);
21095 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21096 v.setStyle('width', '189px');
21100 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21101 if(!this.calendarWeeks){
21106 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21107 v.attr('colspan', function(i, val){
21108 return parseInt(val) + 1;
21113 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21115 this.setStartDate(this.startDate);
21116 this.setEndDate(this.endDate);
21118 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21125 if(this.isInline) {
21130 picker : function()
21132 return this.pickerEl;
21133 // return this.el.select('.datepicker', true).first();
21136 fillDow: function()
21138 var dowCnt = this.weekStart;
21147 if(this.calendarWeeks){
21155 while (dowCnt < this.weekStart + 7) {
21159 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21163 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21166 fillMonths: function()
21169 var months = this.picker().select('>.datepicker-months td', true).first();
21171 months.dom.innerHTML = '';
21177 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21180 months.createChild(month);
21187 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;
21189 if (this.date < this.startDate) {
21190 this.viewDate = new Date(this.startDate);
21191 } else if (this.date > this.endDate) {
21192 this.viewDate = new Date(this.endDate);
21194 this.viewDate = new Date(this.date);
21202 var d = new Date(this.viewDate),
21203 year = d.getUTCFullYear(),
21204 month = d.getUTCMonth(),
21205 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21206 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21207 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21208 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21209 currentDate = this.date && this.date.valueOf(),
21210 today = this.UTCToday();
21212 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21214 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21216 // this.picker.select('>tfoot th.today').
21217 // .text(dates[this.language].today)
21218 // .toggle(this.todayBtn !== false);
21220 this.updateNavArrows();
21223 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21225 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21227 prevMonth.setUTCDate(day);
21229 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21231 var nextMonth = new Date(prevMonth);
21233 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21235 nextMonth = nextMonth.valueOf();
21237 var fillMonths = false;
21239 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21241 while(prevMonth.valueOf() <= nextMonth) {
21244 if (prevMonth.getUTCDay() === this.weekStart) {
21246 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21254 if(this.calendarWeeks){
21255 // ISO 8601: First week contains first thursday.
21256 // ISO also states week starts on Monday, but we can be more abstract here.
21258 // Start of current week: based on weekstart/current date
21259 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21260 // Thursday of this week
21261 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21262 // First Thursday of year, year from thursday
21263 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21264 // Calendar week: ms between thursdays, div ms per day, div 7 days
21265 calWeek = (th - yth) / 864e5 / 7 + 1;
21267 fillMonths.cn.push({
21275 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21277 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21280 if (this.todayHighlight &&
21281 prevMonth.getUTCFullYear() == today.getFullYear() &&
21282 prevMonth.getUTCMonth() == today.getMonth() &&
21283 prevMonth.getUTCDate() == today.getDate()) {
21284 clsName += ' today';
21287 if (currentDate && prevMonth.valueOf() === currentDate) {
21288 clsName += ' active';
21291 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21292 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21293 clsName += ' disabled';
21296 fillMonths.cn.push({
21298 cls: 'day ' + clsName,
21299 html: prevMonth.getDate()
21302 prevMonth.setDate(prevMonth.getDate()+1);
21305 var currentYear = this.date && this.date.getUTCFullYear();
21306 var currentMonth = this.date && this.date.getUTCMonth();
21308 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21310 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21311 v.removeClass('active');
21313 if(currentYear === year && k === currentMonth){
21314 v.addClass('active');
21317 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21318 v.addClass('disabled');
21324 year = parseInt(year/10, 10) * 10;
21326 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21328 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21331 for (var i = -1; i < 11; i++) {
21332 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21334 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21342 showMode: function(dir)
21345 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21348 Roo.each(this.picker().select('>div',true).elements, function(v){
21349 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21352 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21357 if(this.isInline) {
21361 this.picker().removeClass(['bottom', 'top']);
21363 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21365 * place to the top of element!
21369 this.picker().addClass('top');
21370 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21375 this.picker().addClass('bottom');
21377 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21380 parseDate : function(value)
21382 if(!value || value instanceof Date){
21385 var v = Date.parseDate(value, this.format);
21386 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21387 v = Date.parseDate(value, 'Y-m-d');
21389 if(!v && this.altFormats){
21390 if(!this.altFormatsArray){
21391 this.altFormatsArray = this.altFormats.split("|");
21393 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21394 v = Date.parseDate(value, this.altFormatsArray[i]);
21400 formatDate : function(date, fmt)
21402 return (!date || !(date instanceof Date)) ?
21403 date : date.dateFormat(fmt || this.format);
21406 onFocus : function()
21408 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21412 onBlur : function()
21414 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21416 var d = this.inputEl().getValue();
21423 showPopup : function()
21425 this.picker().show();
21429 this.fireEvent('showpopup', this, this.date);
21432 hidePopup : function()
21434 if(this.isInline) {
21437 this.picker().hide();
21438 this.viewMode = this.startViewMode;
21441 this.fireEvent('hidepopup', this, this.date);
21445 onMousedown: function(e)
21447 e.stopPropagation();
21448 e.preventDefault();
21453 Roo.bootstrap.DateField.superclass.keyup.call(this);
21457 setValue: function(v)
21459 if(this.fireEvent('beforeselect', this, v) !== false){
21460 var d = new Date(this.parseDate(v) ).clearTime();
21462 if(isNaN(d.getTime())){
21463 this.date = this.viewDate = '';
21464 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21468 v = this.formatDate(d);
21470 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21472 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21476 this.fireEvent('select', this, this.date);
21480 getValue: function()
21482 return this.formatDate(this.date);
21485 fireKey: function(e)
21487 if (!this.picker().isVisible()){
21488 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21494 var dateChanged = false,
21496 newDate, newViewDate;
21501 e.preventDefault();
21505 if (!this.keyboardNavigation) {
21508 dir = e.keyCode == 37 ? -1 : 1;
21511 newDate = this.moveYear(this.date, dir);
21512 newViewDate = this.moveYear(this.viewDate, dir);
21513 } else if (e.shiftKey){
21514 newDate = this.moveMonth(this.date, dir);
21515 newViewDate = this.moveMonth(this.viewDate, dir);
21517 newDate = new Date(this.date);
21518 newDate.setUTCDate(this.date.getUTCDate() + dir);
21519 newViewDate = new Date(this.viewDate);
21520 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21522 if (this.dateWithinRange(newDate)){
21523 this.date = newDate;
21524 this.viewDate = newViewDate;
21525 this.setValue(this.formatDate(this.date));
21527 e.preventDefault();
21528 dateChanged = true;
21533 if (!this.keyboardNavigation) {
21536 dir = e.keyCode == 38 ? -1 : 1;
21538 newDate = this.moveYear(this.date, dir);
21539 newViewDate = this.moveYear(this.viewDate, dir);
21540 } else if (e.shiftKey){
21541 newDate = this.moveMonth(this.date, dir);
21542 newViewDate = this.moveMonth(this.viewDate, dir);
21544 newDate = new Date(this.date);
21545 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21546 newViewDate = new Date(this.viewDate);
21547 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21549 if (this.dateWithinRange(newDate)){
21550 this.date = newDate;
21551 this.viewDate = newViewDate;
21552 this.setValue(this.formatDate(this.date));
21554 e.preventDefault();
21555 dateChanged = true;
21559 this.setValue(this.formatDate(this.date));
21561 e.preventDefault();
21564 this.setValue(this.formatDate(this.date));
21578 onClick: function(e)
21580 e.stopPropagation();
21581 e.preventDefault();
21583 var target = e.getTarget();
21585 if(target.nodeName.toLowerCase() === 'i'){
21586 target = Roo.get(target).dom.parentNode;
21589 var nodeName = target.nodeName;
21590 var className = target.className;
21591 var html = target.innerHTML;
21592 //Roo.log(nodeName);
21594 switch(nodeName.toLowerCase()) {
21596 switch(className) {
21602 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21603 switch(this.viewMode){
21605 this.viewDate = this.moveMonth(this.viewDate, dir);
21609 this.viewDate = this.moveYear(this.viewDate, dir);
21615 var date = new Date();
21616 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21618 this.setValue(this.formatDate(this.date));
21625 if (className.indexOf('disabled') < 0) {
21626 this.viewDate.setUTCDate(1);
21627 if (className.indexOf('month') > -1) {
21628 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21630 var year = parseInt(html, 10) || 0;
21631 this.viewDate.setUTCFullYear(year);
21635 if(this.singleMode){
21636 this.setValue(this.formatDate(this.viewDate));
21647 //Roo.log(className);
21648 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21649 var day = parseInt(html, 10) || 1;
21650 var year = (this.viewDate || new Date()).getUTCFullYear(),
21651 month = (this.viewDate || new Date()).getUTCMonth();
21653 if (className.indexOf('old') > -1) {
21660 } else if (className.indexOf('new') > -1) {
21668 //Roo.log([year,month,day]);
21669 this.date = this.UTCDate(year, month, day,0,0,0,0);
21670 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21672 //Roo.log(this.formatDate(this.date));
21673 this.setValue(this.formatDate(this.date));
21680 setStartDate: function(startDate)
21682 this.startDate = startDate || -Infinity;
21683 if (this.startDate !== -Infinity) {
21684 this.startDate = this.parseDate(this.startDate);
21687 this.updateNavArrows();
21690 setEndDate: function(endDate)
21692 this.endDate = endDate || Infinity;
21693 if (this.endDate !== Infinity) {
21694 this.endDate = this.parseDate(this.endDate);
21697 this.updateNavArrows();
21700 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21702 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21703 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21704 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21706 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21707 return parseInt(d, 10);
21710 this.updateNavArrows();
21713 updateNavArrows: function()
21715 if(this.singleMode){
21719 var d = new Date(this.viewDate),
21720 year = d.getUTCFullYear(),
21721 month = d.getUTCMonth();
21723 Roo.each(this.picker().select('.prev', true).elements, function(v){
21725 switch (this.viewMode) {
21728 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21734 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21741 Roo.each(this.picker().select('.next', true).elements, function(v){
21743 switch (this.viewMode) {
21746 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21752 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21760 moveMonth: function(date, dir)
21765 var new_date = new Date(date.valueOf()),
21766 day = new_date.getUTCDate(),
21767 month = new_date.getUTCMonth(),
21768 mag = Math.abs(dir),
21770 dir = dir > 0 ? 1 : -1;
21773 // If going back one month, make sure month is not current month
21774 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21776 return new_date.getUTCMonth() == month;
21778 // If going forward one month, make sure month is as expected
21779 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21781 return new_date.getUTCMonth() != new_month;
21783 new_month = month + dir;
21784 new_date.setUTCMonth(new_month);
21785 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21786 if (new_month < 0 || new_month > 11) {
21787 new_month = (new_month + 12) % 12;
21790 // For magnitudes >1, move one month at a time...
21791 for (var i=0; i<mag; i++) {
21792 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21793 new_date = this.moveMonth(new_date, dir);
21795 // ...then reset the day, keeping it in the new month
21796 new_month = new_date.getUTCMonth();
21797 new_date.setUTCDate(day);
21799 return new_month != new_date.getUTCMonth();
21802 // Common date-resetting loop -- if date is beyond end of month, make it
21805 new_date.setUTCDate(--day);
21806 new_date.setUTCMonth(new_month);
21811 moveYear: function(date, dir)
21813 return this.moveMonth(date, dir*12);
21816 dateWithinRange: function(date)
21818 return date >= this.startDate && date <= this.endDate;
21824 this.picker().remove();
21827 validateValue : function(value)
21829 if(this.getVisibilityEl().hasClass('hidden')){
21833 if(value.length < 1) {
21834 if(this.allowBlank){
21840 if(value.length < this.minLength){
21843 if(value.length > this.maxLength){
21847 var vt = Roo.form.VTypes;
21848 if(!vt[this.vtype](value, this)){
21852 if(typeof this.validator == "function"){
21853 var msg = this.validator(value);
21859 if(this.regex && !this.regex.test(value)){
21863 if(typeof(this.parseDate(value)) == 'undefined'){
21867 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21871 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21881 this.date = this.viewDate = '';
21883 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21888 Roo.apply(Roo.bootstrap.DateField, {
21899 html: '<i class="fa fa-arrow-left"/>'
21909 html: '<i class="fa fa-arrow-right"/>'
21951 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21952 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21953 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21954 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21955 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21968 navFnc: 'FullYear',
21973 navFnc: 'FullYear',
21978 Roo.apply(Roo.bootstrap.DateField, {
21982 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21986 cls: 'datepicker-days',
21990 cls: 'table-condensed',
21992 Roo.bootstrap.DateField.head,
21996 Roo.bootstrap.DateField.footer
22003 cls: 'datepicker-months',
22007 cls: 'table-condensed',
22009 Roo.bootstrap.DateField.head,
22010 Roo.bootstrap.DateField.content,
22011 Roo.bootstrap.DateField.footer
22018 cls: 'datepicker-years',
22022 cls: 'table-condensed',
22024 Roo.bootstrap.DateField.head,
22025 Roo.bootstrap.DateField.content,
22026 Roo.bootstrap.DateField.footer
22045 * @class Roo.bootstrap.TimeField
22046 * @extends Roo.bootstrap.Input
22047 * Bootstrap DateField class
22051 * Create a new TimeField
22052 * @param {Object} config The config object
22055 Roo.bootstrap.TimeField = function(config){
22056 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22060 * Fires when this field show.
22061 * @param {Roo.bootstrap.DateField} thisthis
22062 * @param {Mixed} date The date value
22067 * Fires when this field hide.
22068 * @param {Roo.bootstrap.DateField} this
22069 * @param {Mixed} date The date value
22074 * Fires when select a date.
22075 * @param {Roo.bootstrap.DateField} this
22076 * @param {Mixed} date The date value
22082 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22085 * @cfg {String} format
22086 * The default time format string which can be overriden for localization support. The format must be
22087 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22091 getAutoCreate : function()
22093 this.after = '<i class="fa far fa-clock"></i>';
22094 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22098 onRender: function(ct, position)
22101 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22103 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22105 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22107 this.pop = this.picker().select('>.datepicker-time',true).first();
22108 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22110 this.picker().on('mousedown', this.onMousedown, this);
22111 this.picker().on('click', this.onClick, this);
22113 this.picker().addClass('datepicker-dropdown');
22118 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22119 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22120 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22121 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22122 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22123 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22127 fireKey: function(e){
22128 if (!this.picker().isVisible()){
22129 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22135 e.preventDefault();
22143 this.onTogglePeriod();
22146 this.onIncrementMinutes();
22149 this.onDecrementMinutes();
22158 onClick: function(e) {
22159 e.stopPropagation();
22160 e.preventDefault();
22163 picker : function()
22165 return this.pickerEl;
22168 fillTime: function()
22170 var time = this.pop.select('tbody', true).first();
22172 time.dom.innerHTML = '';
22187 cls: 'hours-up fa fas fa-chevron-up'
22207 cls: 'minutes-up fa fas fa-chevron-up'
22228 cls: 'timepicker-hour',
22243 cls: 'timepicker-minute',
22258 cls: 'btn btn-primary period',
22280 cls: 'hours-down fa fas fa-chevron-down'
22300 cls: 'minutes-down fa fas fa-chevron-down'
22318 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22325 var hours = this.time.getHours();
22326 var minutes = this.time.getMinutes();
22339 hours = hours - 12;
22343 hours = '0' + hours;
22347 minutes = '0' + minutes;
22350 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22351 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22352 this.pop.select('button', true).first().dom.innerHTML = period;
22358 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22360 var cls = ['bottom'];
22362 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22369 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22373 //this.picker().setXY(20000,20000);
22374 this.picker().addClass(cls.join('-'));
22378 Roo.each(cls, function(c){
22383 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22384 //_this.picker().setTop(_this.inputEl().getHeight());
22388 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22390 //_this.picker().setTop(0 - _this.picker().getHeight());
22395 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22399 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22407 onFocus : function()
22409 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22413 onBlur : function()
22415 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22421 this.picker().show();
22426 this.fireEvent('show', this, this.date);
22431 this.picker().hide();
22434 this.fireEvent('hide', this, this.date);
22437 setTime : function()
22440 this.setValue(this.time.format(this.format));
22442 this.fireEvent('select', this, this.date);
22447 onMousedown: function(e){
22448 e.stopPropagation();
22449 e.preventDefault();
22452 onIncrementHours: function()
22454 Roo.log('onIncrementHours');
22455 this.time = this.time.add(Date.HOUR, 1);
22460 onDecrementHours: function()
22462 Roo.log('onDecrementHours');
22463 this.time = this.time.add(Date.HOUR, -1);
22467 onIncrementMinutes: function()
22469 Roo.log('onIncrementMinutes');
22470 this.time = this.time.add(Date.MINUTE, 1);
22474 onDecrementMinutes: function()
22476 Roo.log('onDecrementMinutes');
22477 this.time = this.time.add(Date.MINUTE, -1);
22481 onTogglePeriod: function()
22483 Roo.log('onTogglePeriod');
22484 this.time = this.time.add(Date.HOUR, 12);
22492 Roo.apply(Roo.bootstrap.TimeField, {
22496 cls: 'datepicker dropdown-menu',
22500 cls: 'datepicker-time',
22504 cls: 'table-condensed',
22533 cls: 'btn btn-info ok',
22561 * @class Roo.bootstrap.MonthField
22562 * @extends Roo.bootstrap.Input
22563 * Bootstrap MonthField class
22565 * @cfg {String} language default en
22568 * Create a new MonthField
22569 * @param {Object} config The config object
22572 Roo.bootstrap.MonthField = function(config){
22573 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22578 * Fires when this field show.
22579 * @param {Roo.bootstrap.MonthField} this
22580 * @param {Mixed} date The date value
22585 * Fires when this field hide.
22586 * @param {Roo.bootstrap.MonthField} this
22587 * @param {Mixed} date The date value
22592 * Fires when select a date.
22593 * @param {Roo.bootstrap.MonthField} this
22594 * @param {String} oldvalue The old value
22595 * @param {String} newvalue The new value
22601 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22603 onRender: function(ct, position)
22606 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22608 this.language = this.language || 'en';
22609 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22610 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22612 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22613 this.isInline = false;
22614 this.isInput = true;
22615 this.component = this.el.select('.add-on', true).first() || false;
22616 this.component = (this.component && this.component.length === 0) ? false : this.component;
22617 this.hasInput = this.component && this.inputEL().length;
22619 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22621 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22623 this.picker().on('mousedown', this.onMousedown, this);
22624 this.picker().on('click', this.onClick, this);
22626 this.picker().addClass('datepicker-dropdown');
22628 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22629 v.setStyle('width', '189px');
22636 if(this.isInline) {
22642 setValue: function(v, suppressEvent)
22644 var o = this.getValue();
22646 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22650 if(suppressEvent !== true){
22651 this.fireEvent('select', this, o, v);
22656 getValue: function()
22661 onClick: function(e)
22663 e.stopPropagation();
22664 e.preventDefault();
22666 var target = e.getTarget();
22668 if(target.nodeName.toLowerCase() === 'i'){
22669 target = Roo.get(target).dom.parentNode;
22672 var nodeName = target.nodeName;
22673 var className = target.className;
22674 var html = target.innerHTML;
22676 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22680 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22682 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22688 picker : function()
22690 return this.pickerEl;
22693 fillMonths: function()
22696 var months = this.picker().select('>.datepicker-months td', true).first();
22698 months.dom.innerHTML = '';
22704 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22707 months.createChild(month);
22716 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22717 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22720 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22721 e.removeClass('active');
22723 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22724 e.addClass('active');
22731 if(this.isInline) {
22735 this.picker().removeClass(['bottom', 'top']);
22737 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22739 * place to the top of element!
22743 this.picker().addClass('top');
22744 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22749 this.picker().addClass('bottom');
22751 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22754 onFocus : function()
22756 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22760 onBlur : function()
22762 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22764 var d = this.inputEl().getValue();
22773 this.picker().show();
22774 this.picker().select('>.datepicker-months', true).first().show();
22778 this.fireEvent('show', this, this.date);
22783 if(this.isInline) {
22786 this.picker().hide();
22787 this.fireEvent('hide', this, this.date);
22791 onMousedown: function(e)
22793 e.stopPropagation();
22794 e.preventDefault();
22799 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22803 fireKey: function(e)
22805 if (!this.picker().isVisible()){
22806 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22817 e.preventDefault();
22821 dir = e.keyCode == 37 ? -1 : 1;
22823 this.vIndex = this.vIndex + dir;
22825 if(this.vIndex < 0){
22829 if(this.vIndex > 11){
22833 if(isNaN(this.vIndex)){
22837 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22843 dir = e.keyCode == 38 ? -1 : 1;
22845 this.vIndex = this.vIndex + dir * 4;
22847 if(this.vIndex < 0){
22851 if(this.vIndex > 11){
22855 if(isNaN(this.vIndex)){
22859 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22864 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22865 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22869 e.preventDefault();
22872 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22873 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22889 this.picker().remove();
22894 Roo.apply(Roo.bootstrap.MonthField, {
22913 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22914 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22919 Roo.apply(Roo.bootstrap.MonthField, {
22923 cls: 'datepicker dropdown-menu roo-dynamic',
22927 cls: 'datepicker-months',
22931 cls: 'table-condensed',
22933 Roo.bootstrap.DateField.content
22953 * @class Roo.bootstrap.CheckBox
22954 * @extends Roo.bootstrap.Input
22955 * Bootstrap CheckBox class
22957 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22958 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22959 * @cfg {String} boxLabel The text that appears beside the checkbox
22960 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22961 * @cfg {Boolean} checked initnal the element
22962 * @cfg {Boolean} inline inline the element (default false)
22963 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22964 * @cfg {String} tooltip label tooltip
22967 * Create a new CheckBox
22968 * @param {Object} config The config object
22971 Roo.bootstrap.CheckBox = function(config){
22972 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22977 * Fires when the element is checked or unchecked.
22978 * @param {Roo.bootstrap.CheckBox} this This input
22979 * @param {Boolean} checked The new checked value
22984 * Fires when the element is click.
22985 * @param {Roo.bootstrap.CheckBox} this This input
22992 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22994 inputType: 'checkbox',
23003 // checkbox success does not make any sense really..
23008 getAutoCreate : function()
23010 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23016 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23019 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23025 type : this.inputType,
23026 value : this.inputValue,
23027 cls : 'roo-' + this.inputType, //'form-box',
23028 placeholder : this.placeholder || ''
23032 if(this.inputType != 'radio'){
23036 cls : 'roo-hidden-value',
23037 value : this.checked ? this.inputValue : this.valueOff
23042 if (this.weight) { // Validity check?
23043 cfg.cls += " " + this.inputType + "-" + this.weight;
23046 if (this.disabled) {
23047 input.disabled=true;
23051 input.checked = this.checked;
23056 input.name = this.name;
23058 if(this.inputType != 'radio'){
23059 hidden.name = this.name;
23060 input.name = '_hidden_' + this.name;
23065 input.cls += ' input-' + this.size;
23070 ['xs','sm','md','lg'].map(function(size){
23071 if (settings[size]) {
23072 cfg.cls += ' col-' + size + '-' + settings[size];
23076 var inputblock = input;
23078 if (this.before || this.after) {
23081 cls : 'input-group',
23086 inputblock.cn.push({
23088 cls : 'input-group-addon',
23093 inputblock.cn.push(input);
23095 if(this.inputType != 'radio'){
23096 inputblock.cn.push(hidden);
23100 inputblock.cn.push({
23102 cls : 'input-group-addon',
23108 var boxLabelCfg = false;
23114 //'for': id, // box label is handled by onclick - so no for...
23116 html: this.boxLabel
23119 boxLabelCfg.tooltip = this.tooltip;
23125 if (align ==='left' && this.fieldLabel.length) {
23126 // Roo.log("left and has label");
23131 cls : 'control-label',
23132 html : this.fieldLabel
23143 cfg.cn[1].cn.push(boxLabelCfg);
23146 if(this.labelWidth > 12){
23147 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23150 if(this.labelWidth < 13 && this.labelmd == 0){
23151 this.labelmd = this.labelWidth;
23154 if(this.labellg > 0){
23155 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23156 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23159 if(this.labelmd > 0){
23160 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23161 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23164 if(this.labelsm > 0){
23165 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23166 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23169 if(this.labelxs > 0){
23170 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23171 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23174 } else if ( this.fieldLabel.length) {
23175 // Roo.log(" label");
23179 tag: this.boxLabel ? 'span' : 'label',
23181 cls: 'control-label box-input-label',
23182 //cls : 'input-group-addon',
23183 html : this.fieldLabel
23190 cfg.cn.push(boxLabelCfg);
23195 // Roo.log(" no label && no align");
23196 cfg.cn = [ inputblock ] ;
23198 cfg.cn.push(boxLabelCfg);
23206 if(this.inputType != 'radio'){
23207 cfg.cn.push(hidden);
23215 * return the real input element.
23217 inputEl: function ()
23219 return this.el.select('input.roo-' + this.inputType,true).first();
23221 hiddenEl: function ()
23223 return this.el.select('input.roo-hidden-value',true).first();
23226 labelEl: function()
23228 return this.el.select('label.control-label',true).first();
23230 /* depricated... */
23234 return this.labelEl();
23237 boxLabelEl: function()
23239 return this.el.select('label.box-label',true).first();
23242 initEvents : function()
23244 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23246 this.inputEl().on('click', this.onClick, this);
23248 if (this.boxLabel) {
23249 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23252 this.startValue = this.getValue();
23255 Roo.bootstrap.CheckBox.register(this);
23259 onClick : function(e)
23261 if(this.fireEvent('click', this, e) !== false){
23262 this.setChecked(!this.checked);
23267 setChecked : function(state,suppressEvent)
23269 this.startValue = this.getValue();
23271 if(this.inputType == 'radio'){
23273 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23274 e.dom.checked = false;
23277 this.inputEl().dom.checked = true;
23279 this.inputEl().dom.value = this.inputValue;
23281 if(suppressEvent !== true){
23282 this.fireEvent('check', this, true);
23290 this.checked = state;
23292 this.inputEl().dom.checked = state;
23295 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23297 if(suppressEvent !== true){
23298 this.fireEvent('check', this, state);
23304 getValue : function()
23306 if(this.inputType == 'radio'){
23307 return this.getGroupValue();
23310 return this.hiddenEl().dom.value;
23314 getGroupValue : function()
23316 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23320 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23323 setValue : function(v,suppressEvent)
23325 if(this.inputType == 'radio'){
23326 this.setGroupValue(v, suppressEvent);
23330 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23335 setGroupValue : function(v, suppressEvent)
23337 this.startValue = this.getValue();
23339 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23340 e.dom.checked = false;
23342 if(e.dom.value == v){
23343 e.dom.checked = true;
23347 if(suppressEvent !== true){
23348 this.fireEvent('check', this, true);
23356 validate : function()
23358 if(this.getVisibilityEl().hasClass('hidden')){
23364 (this.inputType == 'radio' && this.validateRadio()) ||
23365 (this.inputType == 'checkbox' && this.validateCheckbox())
23371 this.markInvalid();
23375 validateRadio : function()
23377 if(this.getVisibilityEl().hasClass('hidden')){
23381 if(this.allowBlank){
23387 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23388 if(!e.dom.checked){
23400 validateCheckbox : function()
23403 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23404 //return (this.getValue() == this.inputValue) ? true : false;
23407 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23415 for(var i in group){
23416 if(group[i].el.isVisible(true)){
23424 for(var i in group){
23429 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23436 * Mark this field as valid
23438 markValid : function()
23442 this.fireEvent('valid', this);
23444 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23447 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23454 if(this.inputType == 'radio'){
23455 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23456 var fg = e.findParent('.form-group', false, true);
23457 if (Roo.bootstrap.version == 3) {
23458 fg.removeClass([_this.invalidClass, _this.validClass]);
23459 fg.addClass(_this.validClass);
23461 fg.removeClass(['is-valid', 'is-invalid']);
23462 fg.addClass('is-valid');
23470 var fg = this.el.findParent('.form-group', false, true);
23471 if (Roo.bootstrap.version == 3) {
23472 fg.removeClass([this.invalidClass, this.validClass]);
23473 fg.addClass(this.validClass);
23475 fg.removeClass(['is-valid', 'is-invalid']);
23476 fg.addClass('is-valid');
23481 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23487 for(var i in group){
23488 var fg = group[i].el.findParent('.form-group', false, true);
23489 if (Roo.bootstrap.version == 3) {
23490 fg.removeClass([this.invalidClass, this.validClass]);
23491 fg.addClass(this.validClass);
23493 fg.removeClass(['is-valid', 'is-invalid']);
23494 fg.addClass('is-valid');
23500 * Mark this field as invalid
23501 * @param {String} msg The validation message
23503 markInvalid : function(msg)
23505 if(this.allowBlank){
23511 this.fireEvent('invalid', this, msg);
23513 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23516 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23520 label.markInvalid();
23523 if(this.inputType == 'radio'){
23525 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23526 var fg = e.findParent('.form-group', false, true);
23527 if (Roo.bootstrap.version == 3) {
23528 fg.removeClass([_this.invalidClass, _this.validClass]);
23529 fg.addClass(_this.invalidClass);
23531 fg.removeClass(['is-invalid', 'is-valid']);
23532 fg.addClass('is-invalid');
23540 var fg = this.el.findParent('.form-group', false, true);
23541 if (Roo.bootstrap.version == 3) {
23542 fg.removeClass([_this.invalidClass, _this.validClass]);
23543 fg.addClass(_this.invalidClass);
23545 fg.removeClass(['is-invalid', 'is-valid']);
23546 fg.addClass('is-invalid');
23551 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23557 for(var i in group){
23558 var fg = group[i].el.findParent('.form-group', false, true);
23559 if (Roo.bootstrap.version == 3) {
23560 fg.removeClass([_this.invalidClass, _this.validClass]);
23561 fg.addClass(_this.invalidClass);
23563 fg.removeClass(['is-invalid', 'is-valid']);
23564 fg.addClass('is-invalid');
23570 clearInvalid : function()
23572 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23574 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23576 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23578 if (label && label.iconEl) {
23579 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23580 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23584 disable : function()
23586 if(this.inputType != 'radio'){
23587 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23594 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23595 _this.getActionEl().addClass(this.disabledClass);
23596 e.dom.disabled = true;
23600 this.disabled = true;
23601 this.fireEvent("disable", this);
23605 enable : function()
23607 if(this.inputType != 'radio'){
23608 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23615 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23616 _this.getActionEl().removeClass(this.disabledClass);
23617 e.dom.disabled = false;
23621 this.disabled = false;
23622 this.fireEvent("enable", this);
23626 setBoxLabel : function(v)
23631 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23637 Roo.apply(Roo.bootstrap.CheckBox, {
23642 * register a CheckBox Group
23643 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23645 register : function(checkbox)
23647 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23648 this.groups[checkbox.groupId] = {};
23651 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23655 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23659 * fetch a CheckBox Group based on the group ID
23660 * @param {string} the group ID
23661 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23663 get: function(groupId) {
23664 if (typeof(this.groups[groupId]) == 'undefined') {
23668 return this.groups[groupId] ;
23681 * @class Roo.bootstrap.Radio
23682 * @extends Roo.bootstrap.Component
23683 * Bootstrap Radio class
23684 * @cfg {String} boxLabel - the label associated
23685 * @cfg {String} value - the value of radio
23688 * Create a new Radio
23689 * @param {Object} config The config object
23691 Roo.bootstrap.Radio = function(config){
23692 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23696 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23702 getAutoCreate : function()
23706 cls : 'form-group radio',
23711 html : this.boxLabel
23719 initEvents : function()
23721 this.parent().register(this);
23723 this.el.on('click', this.onClick, this);
23727 onClick : function(e)
23729 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23730 this.setChecked(true);
23734 setChecked : function(state, suppressEvent)
23736 this.parent().setValue(this.value, suppressEvent);
23740 setBoxLabel : function(v)
23745 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23760 * @class Roo.bootstrap.SecurePass
23761 * @extends Roo.bootstrap.Input
23762 * Bootstrap SecurePass class
23766 * Create a new SecurePass
23767 * @param {Object} config The config object
23770 Roo.bootstrap.SecurePass = function (config) {
23771 // these go here, so the translation tool can replace them..
23773 PwdEmpty: "Please type a password, and then retype it to confirm.",
23774 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23775 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23776 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23777 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23778 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23779 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23780 TooWeak: "Your password is Too Weak."
23782 this.meterLabel = "Password strength:";
23783 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23784 this.meterClass = [
23785 "roo-password-meter-tooweak",
23786 "roo-password-meter-weak",
23787 "roo-password-meter-medium",
23788 "roo-password-meter-strong",
23789 "roo-password-meter-grey"
23794 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23797 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23799 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23801 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23802 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23803 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23804 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23805 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23806 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23807 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23817 * @cfg {String/Object} Label for the strength meter (defaults to
23818 * 'Password strength:')
23823 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23824 * ['Weak', 'Medium', 'Strong'])
23827 pwdStrengths: false,
23840 initEvents: function ()
23842 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23844 if (this.el.is('input[type=password]') && Roo.isSafari) {
23845 this.el.on('keydown', this.SafariOnKeyDown, this);
23848 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23851 onRender: function (ct, position)
23853 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23854 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23855 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23857 this.trigger.createChild({
23862 cls: 'roo-password-meter-grey col-xs-12',
23865 //width: this.meterWidth + 'px'
23869 cls: 'roo-password-meter-text'
23875 if (this.hideTrigger) {
23876 this.trigger.setDisplayed(false);
23878 this.setSize(this.width || '', this.height || '');
23881 onDestroy: function ()
23883 if (this.trigger) {
23884 this.trigger.removeAllListeners();
23885 this.trigger.remove();
23888 this.wrap.remove();
23890 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23893 checkStrength: function ()
23895 var pwd = this.inputEl().getValue();
23896 if (pwd == this._lastPwd) {
23901 if (this.ClientSideStrongPassword(pwd)) {
23903 } else if (this.ClientSideMediumPassword(pwd)) {
23905 } else if (this.ClientSideWeakPassword(pwd)) {
23911 Roo.log('strength1: ' + strength);
23913 //var pm = this.trigger.child('div/div/div').dom;
23914 var pm = this.trigger.child('div/div');
23915 pm.removeClass(this.meterClass);
23916 pm.addClass(this.meterClass[strength]);
23919 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23921 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23923 this._lastPwd = pwd;
23927 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23929 this._lastPwd = '';
23931 var pm = this.trigger.child('div/div');
23932 pm.removeClass(this.meterClass);
23933 pm.addClass('roo-password-meter-grey');
23936 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23939 this.inputEl().dom.type='password';
23942 validateValue: function (value)
23944 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23947 if (value.length == 0) {
23948 if (this.allowBlank) {
23949 this.clearInvalid();
23953 this.markInvalid(this.errors.PwdEmpty);
23954 this.errorMsg = this.errors.PwdEmpty;
23962 if (!value.match(/[\x21-\x7e]+/)) {
23963 this.markInvalid(this.errors.PwdBadChar);
23964 this.errorMsg = this.errors.PwdBadChar;
23967 if (value.length < 6) {
23968 this.markInvalid(this.errors.PwdShort);
23969 this.errorMsg = this.errors.PwdShort;
23972 if (value.length > 16) {
23973 this.markInvalid(this.errors.PwdLong);
23974 this.errorMsg = this.errors.PwdLong;
23978 if (this.ClientSideStrongPassword(value)) {
23980 } else if (this.ClientSideMediumPassword(value)) {
23982 } else if (this.ClientSideWeakPassword(value)) {
23989 if (strength < 2) {
23990 //this.markInvalid(this.errors.TooWeak);
23991 this.errorMsg = this.errors.TooWeak;
23996 console.log('strength2: ' + strength);
23998 //var pm = this.trigger.child('div/div/div').dom;
24000 var pm = this.trigger.child('div/div');
24001 pm.removeClass(this.meterClass);
24002 pm.addClass(this.meterClass[strength]);
24004 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24006 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24008 this.errorMsg = '';
24012 CharacterSetChecks: function (type)
24015 this.fResult = false;
24018 isctype: function (character, type)
24021 case this.kCapitalLetter:
24022 if (character >= 'A' && character <= 'Z') {
24027 case this.kSmallLetter:
24028 if (character >= 'a' && character <= 'z') {
24034 if (character >= '0' && character <= '9') {
24039 case this.kPunctuation:
24040 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24051 IsLongEnough: function (pwd, size)
24053 return !(pwd == null || isNaN(size) || pwd.length < size);
24056 SpansEnoughCharacterSets: function (word, nb)
24058 if (!this.IsLongEnough(word, nb))
24063 var characterSetChecks = new Array(
24064 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24065 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24068 for (var index = 0; index < word.length; ++index) {
24069 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24070 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24071 characterSetChecks[nCharSet].fResult = true;
24078 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24079 if (characterSetChecks[nCharSet].fResult) {
24084 if (nCharSets < nb) {
24090 ClientSideStrongPassword: function (pwd)
24092 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24095 ClientSideMediumPassword: function (pwd)
24097 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24100 ClientSideWeakPassword: function (pwd)
24102 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24105 })//<script type="text/javascript">
24108 * Based Ext JS Library 1.1.1
24109 * Copyright(c) 2006-2007, Ext JS, LLC.
24115 * @class Roo.HtmlEditorCore
24116 * @extends Roo.Component
24117 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24119 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24122 Roo.HtmlEditorCore = function(config){
24125 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24130 * @event initialize
24131 * Fires when the editor is fully initialized (including the iframe)
24132 * @param {Roo.HtmlEditorCore} this
24137 * Fires when the editor is first receives the focus. Any insertion must wait
24138 * until after this event.
24139 * @param {Roo.HtmlEditorCore} this
24143 * @event beforesync
24144 * Fires before the textarea is updated with content from the editor iframe. Return false
24145 * to cancel the sync.
24146 * @param {Roo.HtmlEditorCore} this
24147 * @param {String} html
24151 * @event beforepush
24152 * Fires before the iframe editor is updated with content from the textarea. Return false
24153 * to cancel the push.
24154 * @param {Roo.HtmlEditorCore} this
24155 * @param {String} html
24160 * Fires when the textarea is updated with content from the editor iframe.
24161 * @param {Roo.HtmlEditorCore} this
24162 * @param {String} html
24167 * Fires when the iframe editor is updated with content from the textarea.
24168 * @param {Roo.HtmlEditorCore} this
24169 * @param {String} html
24174 * @event editorevent
24175 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24176 * @param {Roo.HtmlEditorCore} this
24182 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24184 // defaults : white / black...
24185 this.applyBlacklists();
24192 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24196 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24202 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24207 * @cfg {Number} height (in pixels)
24211 * @cfg {Number} width (in pixels)
24216 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24219 stylesheets: false,
24224 // private properties
24225 validationEvent : false,
24227 initialized : false,
24229 sourceEditMode : false,
24230 onFocus : Roo.emptyFn,
24232 hideMode:'offsets',
24236 // blacklist + whitelisted elements..
24243 * Protected method that will not generally be called directly. It
24244 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24245 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24247 getDocMarkup : function(){
24251 // inherit styels from page...??
24252 if (this.stylesheets === false) {
24254 Roo.get(document.head).select('style').each(function(node) {
24255 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24258 Roo.get(document.head).select('link').each(function(node) {
24259 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24262 } else if (!this.stylesheets.length) {
24264 st = '<style type="text/css">' +
24265 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24268 for (var i in this.stylesheets) {
24269 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24274 st += '<style type="text/css">' +
24275 'IMG { cursor: pointer } ' +
24278 var cls = 'roo-htmleditor-body';
24280 if(this.bodyCls.length){
24281 cls += ' ' + this.bodyCls;
24284 return '<html><head>' + st +
24285 //<style type="text/css">' +
24286 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24288 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24292 onRender : function(ct, position)
24295 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24296 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24299 this.el.dom.style.border = '0 none';
24300 this.el.dom.setAttribute('tabIndex', -1);
24301 this.el.addClass('x-hidden hide');
24305 if(Roo.isIE){ // fix IE 1px bogus margin
24306 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24310 this.frameId = Roo.id();
24314 var iframe = this.owner.wrap.createChild({
24316 cls: 'form-control', // bootstrap..
24318 name: this.frameId,
24319 frameBorder : 'no',
24320 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24325 this.iframe = iframe.dom;
24327 this.assignDocWin();
24329 this.doc.designMode = 'on';
24332 this.doc.write(this.getDocMarkup());
24336 var task = { // must defer to wait for browser to be ready
24338 //console.log("run task?" + this.doc.readyState);
24339 this.assignDocWin();
24340 if(this.doc.body || this.doc.readyState == 'complete'){
24342 this.doc.designMode="on";
24346 Roo.TaskMgr.stop(task);
24347 this.initEditor.defer(10, this);
24354 Roo.TaskMgr.start(task);
24359 onResize : function(w, h)
24361 Roo.log('resize: ' +w + ',' + h );
24362 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24366 if(typeof w == 'number'){
24368 this.iframe.style.width = w + 'px';
24370 if(typeof h == 'number'){
24372 this.iframe.style.height = h + 'px';
24374 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24381 * Toggles the editor between standard and source edit mode.
24382 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24384 toggleSourceEdit : function(sourceEditMode){
24386 this.sourceEditMode = sourceEditMode === true;
24388 if(this.sourceEditMode){
24390 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24393 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24394 //this.iframe.className = '';
24397 //this.setSize(this.owner.wrap.getSize());
24398 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24405 * Protected method that will not generally be called directly. If you need/want
24406 * custom HTML cleanup, this is the method you should override.
24407 * @param {String} html The HTML to be cleaned
24408 * return {String} The cleaned HTML
24410 cleanHtml : function(html){
24411 html = String(html);
24412 if(html.length > 5){
24413 if(Roo.isSafari){ // strip safari nonsense
24414 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24417 if(html == ' '){
24424 * HTML Editor -> Textarea
24425 * Protected method that will not generally be called directly. Syncs the contents
24426 * of the editor iframe with the textarea.
24428 syncValue : function(){
24429 if(this.initialized){
24430 var bd = (this.doc.body || this.doc.documentElement);
24431 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24432 var html = bd.innerHTML;
24434 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24435 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24437 html = '<div style="'+m[0]+'">' + html + '</div>';
24440 html = this.cleanHtml(html);
24441 // fix up the special chars.. normaly like back quotes in word...
24442 // however we do not want to do this with chinese..
24443 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24445 var cc = match.charCodeAt();
24447 // Get the character value, handling surrogate pairs
24448 if (match.length == 2) {
24449 // It's a surrogate pair, calculate the Unicode code point
24450 var high = match.charCodeAt(0) - 0xD800;
24451 var low = match.charCodeAt(1) - 0xDC00;
24452 cc = (high * 0x400) + low + 0x10000;
24454 (cc >= 0x4E00 && cc < 0xA000 ) ||
24455 (cc >= 0x3400 && cc < 0x4E00 ) ||
24456 (cc >= 0xf900 && cc < 0xfb00 )
24461 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24462 return "&#" + cc + ";";
24469 if(this.owner.fireEvent('beforesync', this, html) !== false){
24470 this.el.dom.value = html;
24471 this.owner.fireEvent('sync', this, html);
24477 * Protected method that will not generally be called directly. Pushes the value of the textarea
24478 * into the iframe editor.
24480 pushValue : function(){
24481 if(this.initialized){
24482 var v = this.el.dom.value.trim();
24484 // if(v.length < 1){
24488 if(this.owner.fireEvent('beforepush', this, v) !== false){
24489 var d = (this.doc.body || this.doc.documentElement);
24491 this.cleanUpPaste();
24492 this.el.dom.value = d.innerHTML;
24493 this.owner.fireEvent('push', this, v);
24499 deferFocus : function(){
24500 this.focus.defer(10, this);
24504 focus : function(){
24505 if(this.win && !this.sourceEditMode){
24512 assignDocWin: function()
24514 var iframe = this.iframe;
24517 this.doc = iframe.contentWindow.document;
24518 this.win = iframe.contentWindow;
24520 // if (!Roo.get(this.frameId)) {
24523 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24524 // this.win = Roo.get(this.frameId).dom.contentWindow;
24526 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24530 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24531 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24536 initEditor : function(){
24537 //console.log("INIT EDITOR");
24538 this.assignDocWin();
24542 this.doc.designMode="on";
24544 this.doc.write(this.getDocMarkup());
24547 var dbody = (this.doc.body || this.doc.documentElement);
24548 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24549 // this copies styles from the containing element into thsi one..
24550 // not sure why we need all of this..
24551 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24553 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24554 //ss['background-attachment'] = 'fixed'; // w3c
24555 dbody.bgProperties = 'fixed'; // ie
24556 //Roo.DomHelper.applyStyles(dbody, ss);
24557 Roo.EventManager.on(this.doc, {
24558 //'mousedown': this.onEditorEvent,
24559 'mouseup': this.onEditorEvent,
24560 'dblclick': this.onEditorEvent,
24561 'click': this.onEditorEvent,
24562 'keyup': this.onEditorEvent,
24567 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24569 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24570 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24572 this.initialized = true;
24574 this.owner.fireEvent('initialize', this);
24579 onDestroy : function(){
24585 //for (var i =0; i < this.toolbars.length;i++) {
24586 // // fixme - ask toolbars for heights?
24587 // this.toolbars[i].onDestroy();
24590 //this.wrap.dom.innerHTML = '';
24591 //this.wrap.remove();
24596 onFirstFocus : function(){
24598 this.assignDocWin();
24601 this.activated = true;
24604 if(Roo.isGecko){ // prevent silly gecko errors
24606 var s = this.win.getSelection();
24607 if(!s.focusNode || s.focusNode.nodeType != 3){
24608 var r = s.getRangeAt(0);
24609 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24614 this.execCmd('useCSS', true);
24615 this.execCmd('styleWithCSS', false);
24618 this.owner.fireEvent('activate', this);
24622 adjustFont: function(btn){
24623 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24624 //if(Roo.isSafari){ // safari
24627 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24628 if(Roo.isSafari){ // safari
24629 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24630 v = (v < 10) ? 10 : v;
24631 v = (v > 48) ? 48 : v;
24632 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24637 v = Math.max(1, v+adjust);
24639 this.execCmd('FontSize', v );
24642 onEditorEvent : function(e)
24644 this.owner.fireEvent('editorevent', this, e);
24645 // this.updateToolbar();
24646 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24649 insertTag : function(tg)
24651 // could be a bit smarter... -> wrap the current selected tRoo..
24652 if (tg.toLowerCase() == 'span' ||
24653 tg.toLowerCase() == 'code' ||
24654 tg.toLowerCase() == 'sup' ||
24655 tg.toLowerCase() == 'sub'
24658 range = this.createRange(this.getSelection());
24659 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24660 wrappingNode.appendChild(range.extractContents());
24661 range.insertNode(wrappingNode);
24668 this.execCmd("formatblock", tg);
24672 insertText : function(txt)
24676 var range = this.createRange();
24677 range.deleteContents();
24678 //alert(Sender.getAttribute('label'));
24680 range.insertNode(this.doc.createTextNode(txt));
24686 * Executes a Midas editor command on the editor document and performs necessary focus and
24687 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24688 * @param {String} cmd The Midas command
24689 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24691 relayCmd : function(cmd, value){
24693 this.execCmd(cmd, value);
24694 this.owner.fireEvent('editorevent', this);
24695 //this.updateToolbar();
24696 this.owner.deferFocus();
24700 * Executes a Midas editor command directly on the editor document.
24701 * For visual commands, you should use {@link #relayCmd} instead.
24702 * <b>This should only be called after the editor is initialized.</b>
24703 * @param {String} cmd The Midas command
24704 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24706 execCmd : function(cmd, value){
24707 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24714 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24716 * @param {String} text | dom node..
24718 insertAtCursor : function(text)
24721 if(!this.activated){
24727 var r = this.doc.selection.createRange();
24738 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24742 // from jquery ui (MIT licenced)
24744 var win = this.win;
24746 if (win.getSelection && win.getSelection().getRangeAt) {
24747 range = win.getSelection().getRangeAt(0);
24748 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24749 range.insertNode(node);
24750 } else if (win.document.selection && win.document.selection.createRange) {
24751 // no firefox support
24752 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24753 win.document.selection.createRange().pasteHTML(txt);
24755 // no firefox support
24756 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24757 this.execCmd('InsertHTML', txt);
24766 mozKeyPress : function(e){
24768 var c = e.getCharCode(), cmd;
24771 c = String.fromCharCode(c).toLowerCase();
24785 this.cleanUpPaste.defer(100, this);
24793 e.preventDefault();
24801 fixKeys : function(){ // load time branching for fastest keydown performance
24803 return function(e){
24804 var k = e.getKey(), r;
24807 r = this.doc.selection.createRange();
24810 r.pasteHTML('    ');
24817 r = this.doc.selection.createRange();
24819 var target = r.parentElement();
24820 if(!target || target.tagName.toLowerCase() != 'li'){
24822 r.pasteHTML('<br />');
24828 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24829 this.cleanUpPaste.defer(100, this);
24835 }else if(Roo.isOpera){
24836 return function(e){
24837 var k = e.getKey();
24841 this.execCmd('InsertHTML','    ');
24844 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24845 this.cleanUpPaste.defer(100, this);
24850 }else if(Roo.isSafari){
24851 return function(e){
24852 var k = e.getKey();
24856 this.execCmd('InsertText','\t');
24860 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24861 this.cleanUpPaste.defer(100, this);
24869 getAllAncestors: function()
24871 var p = this.getSelectedNode();
24874 a.push(p); // push blank onto stack..
24875 p = this.getParentElement();
24879 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24883 a.push(this.doc.body);
24887 lastSelNode : false,
24890 getSelection : function()
24892 this.assignDocWin();
24893 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24896 getSelectedNode: function()
24898 // this may only work on Gecko!!!
24900 // should we cache this!!!!
24905 var range = this.createRange(this.getSelection()).cloneRange();
24908 var parent = range.parentElement();
24910 var testRange = range.duplicate();
24911 testRange.moveToElementText(parent);
24912 if (testRange.inRange(range)) {
24915 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24918 parent = parent.parentElement;
24923 // is ancestor a text element.
24924 var ac = range.commonAncestorContainer;
24925 if (ac.nodeType == 3) {
24926 ac = ac.parentNode;
24929 var ar = ac.childNodes;
24932 var other_nodes = [];
24933 var has_other_nodes = false;
24934 for (var i=0;i<ar.length;i++) {
24935 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24938 // fullly contained node.
24940 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24945 // probably selected..
24946 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24947 other_nodes.push(ar[i]);
24951 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24956 has_other_nodes = true;
24958 if (!nodes.length && other_nodes.length) {
24959 nodes= other_nodes;
24961 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24967 createRange: function(sel)
24969 // this has strange effects when using with
24970 // top toolbar - not sure if it's a great idea.
24971 //this.editor.contentWindow.focus();
24972 if (typeof sel != "undefined") {
24974 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24976 return this.doc.createRange();
24979 return this.doc.createRange();
24982 getParentElement: function()
24985 this.assignDocWin();
24986 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24988 var range = this.createRange(sel);
24991 var p = range.commonAncestorContainer;
24992 while (p.nodeType == 3) { // text node
25003 * Range intersection.. the hard stuff...
25007 * [ -- selected range --- ]
25011 * if end is before start or hits it. fail.
25012 * if start is after end or hits it fail.
25014 * if either hits (but other is outside. - then it's not
25020 // @see http://www.thismuchiknow.co.uk/?p=64.
25021 rangeIntersectsNode : function(range, node)
25023 var nodeRange = node.ownerDocument.createRange();
25025 nodeRange.selectNode(node);
25027 nodeRange.selectNodeContents(node);
25030 var rangeStartRange = range.cloneRange();
25031 rangeStartRange.collapse(true);
25033 var rangeEndRange = range.cloneRange();
25034 rangeEndRange.collapse(false);
25036 var nodeStartRange = nodeRange.cloneRange();
25037 nodeStartRange.collapse(true);
25039 var nodeEndRange = nodeRange.cloneRange();
25040 nodeEndRange.collapse(false);
25042 return rangeStartRange.compareBoundaryPoints(
25043 Range.START_TO_START, nodeEndRange) == -1 &&
25044 rangeEndRange.compareBoundaryPoints(
25045 Range.START_TO_START, nodeStartRange) == 1;
25049 rangeCompareNode : function(range, node)
25051 var nodeRange = node.ownerDocument.createRange();
25053 nodeRange.selectNode(node);
25055 nodeRange.selectNodeContents(node);
25059 range.collapse(true);
25061 nodeRange.collapse(true);
25063 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25064 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25066 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25068 var nodeIsBefore = ss == 1;
25069 var nodeIsAfter = ee == -1;
25071 if (nodeIsBefore && nodeIsAfter) {
25074 if (!nodeIsBefore && nodeIsAfter) {
25075 return 1; //right trailed.
25078 if (nodeIsBefore && !nodeIsAfter) {
25079 return 2; // left trailed.
25085 // private? - in a new class?
25086 cleanUpPaste : function()
25088 // cleans up the whole document..
25089 Roo.log('cleanuppaste');
25091 this.cleanUpChildren(this.doc.body);
25092 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25093 if (clean != this.doc.body.innerHTML) {
25094 this.doc.body.innerHTML = clean;
25099 cleanWordChars : function(input) {// change the chars to hex code
25100 var he = Roo.HtmlEditorCore;
25102 var output = input;
25103 Roo.each(he.swapCodes, function(sw) {
25104 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25106 output = output.replace(swapper, sw[1]);
25113 cleanUpChildren : function (n)
25115 if (!n.childNodes.length) {
25118 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25119 this.cleanUpChild(n.childNodes[i]);
25126 cleanUpChild : function (node)
25129 //console.log(node);
25130 if (node.nodeName == "#text") {
25131 // clean up silly Windows -- stuff?
25134 if (node.nodeName == "#comment") {
25135 node.parentNode.removeChild(node);
25136 // clean up silly Windows -- stuff?
25139 var lcname = node.tagName.toLowerCase();
25140 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25141 // whitelist of tags..
25143 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25145 node.parentNode.removeChild(node);
25150 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25152 // spans with no attributes - just remove them..
25153 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25154 remove_keep_children = true;
25157 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25158 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25160 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25161 // remove_keep_children = true;
25164 if (remove_keep_children) {
25165 this.cleanUpChildren(node);
25166 // inserts everything just before this node...
25167 while (node.childNodes.length) {
25168 var cn = node.childNodes[0];
25169 node.removeChild(cn);
25170 node.parentNode.insertBefore(cn, node);
25172 node.parentNode.removeChild(node);
25176 if (!node.attributes || !node.attributes.length) {
25181 this.cleanUpChildren(node);
25185 function cleanAttr(n,v)
25188 if (v.match(/^\./) || v.match(/^\//)) {
25191 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25194 if (v.match(/^#/)) {
25197 if (v.match(/^\{/)) { // allow template editing.
25200 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25201 node.removeAttribute(n);
25205 var cwhite = this.cwhite;
25206 var cblack = this.cblack;
25208 function cleanStyle(n,v)
25210 if (v.match(/expression/)) { //XSS?? should we even bother..
25211 node.removeAttribute(n);
25215 var parts = v.split(/;/);
25218 Roo.each(parts, function(p) {
25219 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25223 var l = p.split(':').shift().replace(/\s+/g,'');
25224 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25226 if ( cwhite.length && cblack.indexOf(l) > -1) {
25227 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25228 //node.removeAttribute(n);
25232 // only allow 'c whitelisted system attributes'
25233 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25234 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25235 //node.removeAttribute(n);
25245 if (clean.length) {
25246 node.setAttribute(n, clean.join(';'));
25248 node.removeAttribute(n);
25254 for (var i = node.attributes.length-1; i > -1 ; i--) {
25255 var a = node.attributes[i];
25258 if (a.name.toLowerCase().substr(0,2)=='on') {
25259 node.removeAttribute(a.name);
25262 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25263 node.removeAttribute(a.name);
25266 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25267 cleanAttr(a.name,a.value); // fixme..
25270 if (a.name == 'style') {
25271 cleanStyle(a.name,a.value);
25274 /// clean up MS crap..
25275 // tecnically this should be a list of valid class'es..
25278 if (a.name == 'class') {
25279 if (a.value.match(/^Mso/)) {
25280 node.removeAttribute('class');
25283 if (a.value.match(/^body$/)) {
25284 node.removeAttribute('class');
25295 this.cleanUpChildren(node);
25301 * Clean up MS wordisms...
25303 cleanWord : function(node)
25306 this.cleanWord(this.doc.body);
25311 node.nodeName == 'SPAN' &&
25312 !node.hasAttributes() &&
25313 node.childNodes.length == 1 &&
25314 node.firstChild.nodeName == "#text"
25316 var textNode = node.firstChild;
25317 node.removeChild(textNode);
25318 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25319 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25321 node.parentNode.insertBefore(textNode, node);
25322 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25323 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25325 node.parentNode.removeChild(node);
25328 if (node.nodeName == "#text") {
25329 // clean up silly Windows -- stuff?
25332 if (node.nodeName == "#comment") {
25333 node.parentNode.removeChild(node);
25334 // clean up silly Windows -- stuff?
25338 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25339 node.parentNode.removeChild(node);
25342 //Roo.log(node.tagName);
25343 // remove - but keep children..
25344 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25345 //Roo.log('-- removed');
25346 while (node.childNodes.length) {
25347 var cn = node.childNodes[0];
25348 node.removeChild(cn);
25349 node.parentNode.insertBefore(cn, node);
25350 // move node to parent - and clean it..
25351 this.cleanWord(cn);
25353 node.parentNode.removeChild(node);
25354 /// no need to iterate chidlren = it's got none..
25355 //this.iterateChildren(node, this.cleanWord);
25359 if (node.className.length) {
25361 var cn = node.className.split(/\W+/);
25363 Roo.each(cn, function(cls) {
25364 if (cls.match(/Mso[a-zA-Z]+/)) {
25369 node.className = cna.length ? cna.join(' ') : '';
25371 node.removeAttribute("class");
25375 if (node.hasAttribute("lang")) {
25376 node.removeAttribute("lang");
25379 if (node.hasAttribute("style")) {
25381 var styles = node.getAttribute("style").split(";");
25383 Roo.each(styles, function(s) {
25384 if (!s.match(/:/)) {
25387 var kv = s.split(":");
25388 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25391 // what ever is left... we allow.
25394 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25395 if (!nstyle.length) {
25396 node.removeAttribute('style');
25399 this.iterateChildren(node, this.cleanWord);
25405 * iterateChildren of a Node, calling fn each time, using this as the scole..
25406 * @param {DomNode} node node to iterate children of.
25407 * @param {Function} fn method of this class to call on each item.
25409 iterateChildren : function(node, fn)
25411 if (!node.childNodes.length) {
25414 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25415 fn.call(this, node.childNodes[i])
25421 * cleanTableWidths.
25423 * Quite often pasting from word etc.. results in tables with column and widths.
25424 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25427 cleanTableWidths : function(node)
25432 this.cleanTableWidths(this.doc.body);
25437 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25440 Roo.log(node.tagName);
25441 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25442 this.iterateChildren(node, this.cleanTableWidths);
25445 if (node.hasAttribute('width')) {
25446 node.removeAttribute('width');
25450 if (node.hasAttribute("style")) {
25453 var styles = node.getAttribute("style").split(";");
25455 Roo.each(styles, function(s) {
25456 if (!s.match(/:/)) {
25459 var kv = s.split(":");
25460 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25463 // what ever is left... we allow.
25466 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25467 if (!nstyle.length) {
25468 node.removeAttribute('style');
25472 this.iterateChildren(node, this.cleanTableWidths);
25480 domToHTML : function(currentElement, depth, nopadtext) {
25482 depth = depth || 0;
25483 nopadtext = nopadtext || false;
25485 if (!currentElement) {
25486 return this.domToHTML(this.doc.body);
25489 //Roo.log(currentElement);
25491 var allText = false;
25492 var nodeName = currentElement.nodeName;
25493 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25495 if (nodeName == '#text') {
25497 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25502 if (nodeName != 'BODY') {
25505 // Prints the node tagName, such as <A>, <IMG>, etc
25508 for(i = 0; i < currentElement.attributes.length;i++) {
25510 var aname = currentElement.attributes.item(i).name;
25511 if (!currentElement.attributes.item(i).value.length) {
25514 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25517 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25526 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25529 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25534 // Traverse the tree
25536 var currentElementChild = currentElement.childNodes.item(i);
25537 var allText = true;
25538 var innerHTML = '';
25540 while (currentElementChild) {
25541 // Formatting code (indent the tree so it looks nice on the screen)
25542 var nopad = nopadtext;
25543 if (lastnode == 'SPAN') {
25547 if (currentElementChild.nodeName == '#text') {
25548 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25549 toadd = nopadtext ? toadd : toadd.trim();
25550 if (!nopad && toadd.length > 80) {
25551 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25553 innerHTML += toadd;
25556 currentElementChild = currentElement.childNodes.item(i);
25562 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25564 // Recursively traverse the tree structure of the child node
25565 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25566 lastnode = currentElementChild.nodeName;
25568 currentElementChild=currentElement.childNodes.item(i);
25574 // The remaining code is mostly for formatting the tree
25575 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25580 ret+= "</"+tagName+">";
25586 applyBlacklists : function()
25588 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25589 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25593 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25594 if (b.indexOf(tag) > -1) {
25597 this.white.push(tag);
25601 Roo.each(w, function(tag) {
25602 if (b.indexOf(tag) > -1) {
25605 if (this.white.indexOf(tag) > -1) {
25608 this.white.push(tag);
25613 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25614 if (w.indexOf(tag) > -1) {
25617 this.black.push(tag);
25621 Roo.each(b, function(tag) {
25622 if (w.indexOf(tag) > -1) {
25625 if (this.black.indexOf(tag) > -1) {
25628 this.black.push(tag);
25633 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25634 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25638 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25639 if (b.indexOf(tag) > -1) {
25642 this.cwhite.push(tag);
25646 Roo.each(w, function(tag) {
25647 if (b.indexOf(tag) > -1) {
25650 if (this.cwhite.indexOf(tag) > -1) {
25653 this.cwhite.push(tag);
25658 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25659 if (w.indexOf(tag) > -1) {
25662 this.cblack.push(tag);
25666 Roo.each(b, function(tag) {
25667 if (w.indexOf(tag) > -1) {
25670 if (this.cblack.indexOf(tag) > -1) {
25673 this.cblack.push(tag);
25678 setStylesheets : function(stylesheets)
25680 if(typeof(stylesheets) == 'string'){
25681 Roo.get(this.iframe.contentDocument.head).createChild({
25683 rel : 'stylesheet',
25692 Roo.each(stylesheets, function(s) {
25697 Roo.get(_this.iframe.contentDocument.head).createChild({
25699 rel : 'stylesheet',
25708 removeStylesheets : function()
25712 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25717 setStyle : function(style)
25719 Roo.get(this.iframe.contentDocument.head).createChild({
25728 // hide stuff that is not compatible
25742 * @event specialkey
25746 * @cfg {String} fieldClass @hide
25749 * @cfg {String} focusClass @hide
25752 * @cfg {String} autoCreate @hide
25755 * @cfg {String} inputType @hide
25758 * @cfg {String} invalidClass @hide
25761 * @cfg {String} invalidText @hide
25764 * @cfg {String} msgFx @hide
25767 * @cfg {String} validateOnBlur @hide
25771 Roo.HtmlEditorCore.white = [
25772 'area', 'br', 'img', 'input', 'hr', 'wbr',
25774 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25775 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25776 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25777 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25778 'table', 'ul', 'xmp',
25780 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25783 'dir', 'menu', 'ol', 'ul', 'dl',
25789 Roo.HtmlEditorCore.black = [
25790 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25792 'base', 'basefont', 'bgsound', 'blink', 'body',
25793 'frame', 'frameset', 'head', 'html', 'ilayer',
25794 'iframe', 'layer', 'link', 'meta', 'object',
25795 'script', 'style' ,'title', 'xml' // clean later..
25797 Roo.HtmlEditorCore.clean = [
25798 'script', 'style', 'title', 'xml'
25800 Roo.HtmlEditorCore.remove = [
25805 Roo.HtmlEditorCore.ablack = [
25809 Roo.HtmlEditorCore.aclean = [
25810 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25814 Roo.HtmlEditorCore.pwhite= [
25815 'http', 'https', 'mailto'
25818 // white listed style attributes.
25819 Roo.HtmlEditorCore.cwhite= [
25820 // 'text-align', /// default is to allow most things..
25826 // black listed style attributes.
25827 Roo.HtmlEditorCore.cblack= [
25828 // 'font-size' -- this can be set by the project
25832 Roo.HtmlEditorCore.swapCodes =[
25851 * @class Roo.bootstrap.HtmlEditor
25852 * @extends Roo.bootstrap.TextArea
25853 * Bootstrap HtmlEditor class
25856 * Create a new HtmlEditor
25857 * @param {Object} config The config object
25860 Roo.bootstrap.HtmlEditor = function(config){
25861 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25862 if (!this.toolbars) {
25863 this.toolbars = [];
25866 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25869 * @event initialize
25870 * Fires when the editor is fully initialized (including the iframe)
25871 * @param {HtmlEditor} this
25876 * Fires when the editor is first receives the focus. Any insertion must wait
25877 * until after this event.
25878 * @param {HtmlEditor} this
25882 * @event beforesync
25883 * Fires before the textarea is updated with content from the editor iframe. Return false
25884 * to cancel the sync.
25885 * @param {HtmlEditor} this
25886 * @param {String} html
25890 * @event beforepush
25891 * Fires before the iframe editor is updated with content from the textarea. Return false
25892 * to cancel the push.
25893 * @param {HtmlEditor} this
25894 * @param {String} html
25899 * Fires when the textarea is updated with content from the editor iframe.
25900 * @param {HtmlEditor} this
25901 * @param {String} html
25906 * Fires when the iframe editor is updated with content from the textarea.
25907 * @param {HtmlEditor} this
25908 * @param {String} html
25912 * @event editmodechange
25913 * Fires when the editor switches edit modes
25914 * @param {HtmlEditor} this
25915 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25917 editmodechange: true,
25919 * @event editorevent
25920 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25921 * @param {HtmlEditor} this
25925 * @event firstfocus
25926 * Fires when on first focus - needed by toolbars..
25927 * @param {HtmlEditor} this
25932 * Auto save the htmlEditor value as a file into Events
25933 * @param {HtmlEditor} this
25937 * @event savedpreview
25938 * preview the saved version of htmlEditor
25939 * @param {HtmlEditor} this
25946 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25950 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25955 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25960 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25965 * @cfg {Number} height (in pixels)
25969 * @cfg {Number} width (in pixels)
25974 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25977 stylesheets: false,
25982 // private properties
25983 validationEvent : false,
25985 initialized : false,
25988 onFocus : Roo.emptyFn,
25990 hideMode:'offsets',
25992 tbContainer : false,
25996 toolbarContainer :function() {
25997 return this.wrap.select('.x-html-editor-tb',true).first();
26001 * Protected method that will not generally be called directly. It
26002 * is called when the editor creates its toolbar. Override this method if you need to
26003 * add custom toolbar buttons.
26004 * @param {HtmlEditor} editor
26006 createToolbar : function(){
26007 Roo.log('renewing');
26008 Roo.log("create toolbars");
26010 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26011 this.toolbars[0].render(this.toolbarContainer());
26015 // if (!editor.toolbars || !editor.toolbars.length) {
26016 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26019 // for (var i =0 ; i < editor.toolbars.length;i++) {
26020 // editor.toolbars[i] = Roo.factory(
26021 // typeof(editor.toolbars[i]) == 'string' ?
26022 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26023 // Roo.bootstrap.HtmlEditor);
26024 // editor.toolbars[i].init(editor);
26030 onRender : function(ct, position)
26032 // Roo.log("Call onRender: " + this.xtype);
26034 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26036 this.wrap = this.inputEl().wrap({
26037 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26040 this.editorcore.onRender(ct, position);
26042 if (this.resizable) {
26043 this.resizeEl = new Roo.Resizable(this.wrap, {
26047 minHeight : this.height,
26048 height: this.height,
26049 handles : this.resizable,
26052 resize : function(r, w, h) {
26053 _t.onResize(w,h); // -something
26059 this.createToolbar(this);
26062 if(!this.width && this.resizable){
26063 this.setSize(this.wrap.getSize());
26065 if (this.resizeEl) {
26066 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26067 // should trigger onReize..
26073 onResize : function(w, h)
26075 Roo.log('resize: ' +w + ',' + h );
26076 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26080 if(this.inputEl() ){
26081 if(typeof w == 'number'){
26082 var aw = w - this.wrap.getFrameWidth('lr');
26083 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26086 if(typeof h == 'number'){
26087 var tbh = -11; // fixme it needs to tool bar size!
26088 for (var i =0; i < this.toolbars.length;i++) {
26089 // fixme - ask toolbars for heights?
26090 tbh += this.toolbars[i].el.getHeight();
26091 //if (this.toolbars[i].footer) {
26092 // tbh += this.toolbars[i].footer.el.getHeight();
26100 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26101 ah -= 5; // knock a few pixes off for look..
26102 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26106 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26107 this.editorcore.onResize(ew,eh);
26112 * Toggles the editor between standard and source edit mode.
26113 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26115 toggleSourceEdit : function(sourceEditMode)
26117 this.editorcore.toggleSourceEdit(sourceEditMode);
26119 if(this.editorcore.sourceEditMode){
26120 Roo.log('editor - showing textarea');
26123 // Roo.log(this.syncValue());
26125 this.inputEl().removeClass(['hide', 'x-hidden']);
26126 this.inputEl().dom.removeAttribute('tabIndex');
26127 this.inputEl().focus();
26129 Roo.log('editor - hiding textarea');
26131 // Roo.log(this.pushValue());
26134 this.inputEl().addClass(['hide', 'x-hidden']);
26135 this.inputEl().dom.setAttribute('tabIndex', -1);
26136 //this.deferFocus();
26139 if(this.resizable){
26140 this.setSize(this.wrap.getSize());
26143 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26146 // private (for BoxComponent)
26147 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26149 // private (for BoxComponent)
26150 getResizeEl : function(){
26154 // private (for BoxComponent)
26155 getPositionEl : function(){
26160 initEvents : function(){
26161 this.originalValue = this.getValue();
26165 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26168 // markInvalid : Roo.emptyFn,
26170 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26173 // clearInvalid : Roo.emptyFn,
26175 setValue : function(v){
26176 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26177 this.editorcore.pushValue();
26182 deferFocus : function(){
26183 this.focus.defer(10, this);
26187 focus : function(){
26188 this.editorcore.focus();
26194 onDestroy : function(){
26200 for (var i =0; i < this.toolbars.length;i++) {
26201 // fixme - ask toolbars for heights?
26202 this.toolbars[i].onDestroy();
26205 this.wrap.dom.innerHTML = '';
26206 this.wrap.remove();
26211 onFirstFocus : function(){
26212 //Roo.log("onFirstFocus");
26213 this.editorcore.onFirstFocus();
26214 for (var i =0; i < this.toolbars.length;i++) {
26215 this.toolbars[i].onFirstFocus();
26221 syncValue : function()
26223 this.editorcore.syncValue();
26226 pushValue : function()
26228 this.editorcore.pushValue();
26232 // hide stuff that is not compatible
26246 * @event specialkey
26250 * @cfg {String} fieldClass @hide
26253 * @cfg {String} focusClass @hide
26256 * @cfg {String} autoCreate @hide
26259 * @cfg {String} inputType @hide
26263 * @cfg {String} invalidText @hide
26266 * @cfg {String} msgFx @hide
26269 * @cfg {String} validateOnBlur @hide
26278 Roo.namespace('Roo.bootstrap.htmleditor');
26280 * @class Roo.bootstrap.HtmlEditorToolbar1
26286 new Roo.bootstrap.HtmlEditor({
26289 new Roo.bootstrap.HtmlEditorToolbar1({
26290 disable : { fonts: 1 , format: 1, ..., ... , ...],
26296 * @cfg {Object} disable List of elements to disable..
26297 * @cfg {Array} btns List of additional buttons.
26301 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26304 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26307 Roo.apply(this, config);
26309 // default disabled, based on 'good practice'..
26310 this.disable = this.disable || {};
26311 Roo.applyIf(this.disable, {
26314 specialElements : true
26316 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26318 this.editor = config.editor;
26319 this.editorcore = config.editor.editorcore;
26321 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26323 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26324 // dont call parent... till later.
26326 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26331 editorcore : false,
26336 "h1","h2","h3","h4","h5","h6",
26338 "abbr", "acronym", "address", "cite", "samp", "var",
26342 onRender : function(ct, position)
26344 // Roo.log("Call onRender: " + this.xtype);
26346 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26348 this.el.dom.style.marginBottom = '0';
26350 var editorcore = this.editorcore;
26351 var editor= this.editor;
26354 var btn = function(id,cmd , toggle, handler, html){
26356 var event = toggle ? 'toggle' : 'click';
26361 xns: Roo.bootstrap,
26365 enableToggle:toggle !== false,
26367 pressed : toggle ? false : null,
26370 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26371 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26377 // var cb_box = function...
26382 xns: Roo.bootstrap,
26387 xns: Roo.bootstrap,
26391 Roo.each(this.formats, function(f) {
26392 style.menu.items.push({
26394 xns: Roo.bootstrap,
26395 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26400 editorcore.insertTag(this.tagname);
26407 children.push(style);
26409 btn('bold',false,true);
26410 btn('italic',false,true);
26411 btn('align-left', 'justifyleft',true);
26412 btn('align-center', 'justifycenter',true);
26413 btn('align-right' , 'justifyright',true);
26414 btn('link', false, false, function(btn) {
26415 //Roo.log("create link?");
26416 var url = prompt(this.createLinkText, this.defaultLinkValue);
26417 if(url && url != 'http:/'+'/'){
26418 this.editorcore.relayCmd('createlink', url);
26421 btn('list','insertunorderedlist',true);
26422 btn('pencil', false,true, function(btn){
26424 this.toggleSourceEdit(btn.pressed);
26427 if (this.editor.btns.length > 0) {
26428 for (var i = 0; i<this.editor.btns.length; i++) {
26429 children.push(this.editor.btns[i]);
26437 xns: Roo.bootstrap,
26442 xns: Roo.bootstrap,
26447 cog.menu.items.push({
26449 xns: Roo.bootstrap,
26450 html : Clean styles,
26455 editorcore.insertTag(this.tagname);
26464 this.xtype = 'NavSimplebar';
26466 for(var i=0;i< children.length;i++) {
26468 this.buttons.add(this.addxtypeChild(children[i]));
26472 editor.on('editorevent', this.updateToolbar, this);
26474 onBtnClick : function(id)
26476 this.editorcore.relayCmd(id);
26477 this.editorcore.focus();
26481 * Protected method that will not generally be called directly. It triggers
26482 * a toolbar update by reading the markup state of the current selection in the editor.
26484 updateToolbar: function(){
26486 if(!this.editorcore.activated){
26487 this.editor.onFirstFocus(); // is this neeed?
26491 var btns = this.buttons;
26492 var doc = this.editorcore.doc;
26493 btns.get('bold').setActive(doc.queryCommandState('bold'));
26494 btns.get('italic').setActive(doc.queryCommandState('italic'));
26495 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26497 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26498 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26499 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26501 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26502 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26505 var ans = this.editorcore.getAllAncestors();
26506 if (this.formatCombo) {
26509 var store = this.formatCombo.store;
26510 this.formatCombo.setValue("");
26511 for (var i =0; i < ans.length;i++) {
26512 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26514 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26522 // hides menus... - so this cant be on a menu...
26523 Roo.bootstrap.MenuMgr.hideAll();
26525 Roo.bootstrap.MenuMgr.hideAll();
26526 //this.editorsyncValue();
26528 onFirstFocus: function() {
26529 this.buttons.each(function(item){
26533 toggleSourceEdit : function(sourceEditMode){
26536 if(sourceEditMode){
26537 Roo.log("disabling buttons");
26538 this.buttons.each( function(item){
26539 if(item.cmd != 'pencil'){
26545 Roo.log("enabling buttons");
26546 if(this.editorcore.initialized){
26547 this.buttons.each( function(item){
26553 Roo.log("calling toggole on editor");
26554 // tell the editor that it's been pressed..
26555 this.editor.toggleSourceEdit(sourceEditMode);
26569 * @class Roo.bootstrap.Markdown
26570 * @extends Roo.bootstrap.TextArea
26571 * Bootstrap Showdown editable area
26572 * @cfg {string} content
26575 * Create a new Showdown
26578 Roo.bootstrap.Markdown = function(config){
26579 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26583 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26587 initEvents : function()
26590 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26591 this.markdownEl = this.el.createChild({
26592 cls : 'roo-markdown-area'
26594 this.inputEl().addClass('d-none');
26595 if (this.getValue() == '') {
26596 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26599 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26601 this.markdownEl.on('click', this.toggleTextEdit, this);
26602 this.on('blur', this.toggleTextEdit, this);
26603 this.on('specialkey', this.resizeTextArea, this);
26606 toggleTextEdit : function()
26608 var sh = this.markdownEl.getHeight();
26609 this.inputEl().addClass('d-none');
26610 this.markdownEl.addClass('d-none');
26611 if (!this.editing) {
26613 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26614 this.inputEl().removeClass('d-none');
26615 this.inputEl().focus();
26616 this.editing = true;
26619 // show showdown...
26620 this.updateMarkdown();
26621 this.markdownEl.removeClass('d-none');
26622 this.editing = false;
26625 updateMarkdown : function()
26627 if (this.getValue() == '') {
26628 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26632 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26635 resizeTextArea: function () {
26638 Roo.log([sh, this.getValue().split("\n").length * 30]);
26639 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26641 setValue : function(val)
26643 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26644 if (!this.editing) {
26645 this.updateMarkdown();
26651 if (!this.editing) {
26652 this.toggleTextEdit();
26660 * @class Roo.bootstrap.Table.AbstractSelectionModel
26661 * @extends Roo.util.Observable
26662 * Abstract base class for grid SelectionModels. It provides the interface that should be
26663 * implemented by descendant classes. This class should not be directly instantiated.
26666 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26667 this.locked = false;
26668 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26672 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26673 /** @ignore Called by the grid automatically. Do not call directly. */
26674 init : function(grid){
26680 * Locks the selections.
26683 this.locked = true;
26687 * Unlocks the selections.
26689 unlock : function(){
26690 this.locked = false;
26694 * Returns true if the selections are locked.
26695 * @return {Boolean}
26697 isLocked : function(){
26698 return this.locked;
26702 initEvents : function ()
26708 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26709 * @class Roo.bootstrap.Table.RowSelectionModel
26710 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26711 * It supports multiple selections and keyboard selection/navigation.
26713 * @param {Object} config
26716 Roo.bootstrap.Table.RowSelectionModel = function(config){
26717 Roo.apply(this, config);
26718 this.selections = new Roo.util.MixedCollection(false, function(o){
26723 this.lastActive = false;
26727 * @event selectionchange
26728 * Fires when the selection changes
26729 * @param {SelectionModel} this
26731 "selectionchange" : true,
26733 * @event afterselectionchange
26734 * Fires after the selection changes (eg. by key press or clicking)
26735 * @param {SelectionModel} this
26737 "afterselectionchange" : true,
26739 * @event beforerowselect
26740 * Fires when a row is selected being selected, return false to cancel.
26741 * @param {SelectionModel} this
26742 * @param {Number} rowIndex The selected index
26743 * @param {Boolean} keepExisting False if other selections will be cleared
26745 "beforerowselect" : true,
26748 * Fires when a row is selected.
26749 * @param {SelectionModel} this
26750 * @param {Number} rowIndex The selected index
26751 * @param {Roo.data.Record} r The record
26753 "rowselect" : true,
26755 * @event rowdeselect
26756 * Fires when a row is deselected.
26757 * @param {SelectionModel} this
26758 * @param {Number} rowIndex The selected index
26760 "rowdeselect" : true
26762 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26763 this.locked = false;
26766 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26768 * @cfg {Boolean} singleSelect
26769 * True to allow selection of only one row at a time (defaults to false)
26771 singleSelect : false,
26774 initEvents : function()
26777 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26778 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26779 //}else{ // allow click to work like normal
26780 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26782 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26783 this.grid.on("rowclick", this.handleMouseDown, this);
26785 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26786 "up" : function(e){
26788 this.selectPrevious(e.shiftKey);
26789 }else if(this.last !== false && this.lastActive !== false){
26790 var last = this.last;
26791 this.selectRange(this.last, this.lastActive-1);
26792 this.grid.getView().focusRow(this.lastActive);
26793 if(last !== false){
26797 this.selectFirstRow();
26799 this.fireEvent("afterselectionchange", this);
26801 "down" : function(e){
26803 this.selectNext(e.shiftKey);
26804 }else if(this.last !== false && this.lastActive !== false){
26805 var last = this.last;
26806 this.selectRange(this.last, this.lastActive+1);
26807 this.grid.getView().focusRow(this.lastActive);
26808 if(last !== false){
26812 this.selectFirstRow();
26814 this.fireEvent("afterselectionchange", this);
26818 this.grid.store.on('load', function(){
26819 this.selections.clear();
26822 var view = this.grid.view;
26823 view.on("refresh", this.onRefresh, this);
26824 view.on("rowupdated", this.onRowUpdated, this);
26825 view.on("rowremoved", this.onRemove, this);
26830 onRefresh : function()
26832 var ds = this.grid.store, i, v = this.grid.view;
26833 var s = this.selections;
26834 s.each(function(r){
26835 if((i = ds.indexOfId(r.id)) != -1){
26844 onRemove : function(v, index, r){
26845 this.selections.remove(r);
26849 onRowUpdated : function(v, index, r){
26850 if(this.isSelected(r)){
26851 v.onRowSelect(index);
26857 * @param {Array} records The records to select
26858 * @param {Boolean} keepExisting (optional) True to keep existing selections
26860 selectRecords : function(records, keepExisting)
26863 this.clearSelections();
26865 var ds = this.grid.store;
26866 for(var i = 0, len = records.length; i < len; i++){
26867 this.selectRow(ds.indexOf(records[i]), true);
26872 * Gets the number of selected rows.
26875 getCount : function(){
26876 return this.selections.length;
26880 * Selects the first row in the grid.
26882 selectFirstRow : function(){
26887 * Select the last row.
26888 * @param {Boolean} keepExisting (optional) True to keep existing selections
26890 selectLastRow : function(keepExisting){
26891 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26892 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26896 * Selects the row immediately following the last selected row.
26897 * @param {Boolean} keepExisting (optional) True to keep existing selections
26899 selectNext : function(keepExisting)
26901 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26902 this.selectRow(this.last+1, keepExisting);
26903 this.grid.getView().focusRow(this.last);
26908 * Selects the row that precedes the last selected row.
26909 * @param {Boolean} keepExisting (optional) True to keep existing selections
26911 selectPrevious : function(keepExisting){
26913 this.selectRow(this.last-1, keepExisting);
26914 this.grid.getView().focusRow(this.last);
26919 * Returns the selected records
26920 * @return {Array} Array of selected records
26922 getSelections : function(){
26923 return [].concat(this.selections.items);
26927 * Returns the first selected record.
26930 getSelected : function(){
26931 return this.selections.itemAt(0);
26936 * Clears all selections.
26938 clearSelections : function(fast)
26944 var ds = this.grid.store;
26945 var s = this.selections;
26946 s.each(function(r){
26947 this.deselectRow(ds.indexOfId(r.id));
26951 this.selections.clear();
26958 * Selects all rows.
26960 selectAll : function(){
26964 this.selections.clear();
26965 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26966 this.selectRow(i, true);
26971 * Returns True if there is a selection.
26972 * @return {Boolean}
26974 hasSelection : function(){
26975 return this.selections.length > 0;
26979 * Returns True if the specified row is selected.
26980 * @param {Number/Record} record The record or index of the record to check
26981 * @return {Boolean}
26983 isSelected : function(index){
26984 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26985 return (r && this.selections.key(r.id) ? true : false);
26989 * Returns True if the specified record id is selected.
26990 * @param {String} id The id of record to check
26991 * @return {Boolean}
26993 isIdSelected : function(id){
26994 return (this.selections.key(id) ? true : false);
26999 handleMouseDBClick : function(e, t){
27003 handleMouseDown : function(e, t)
27005 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27006 if(this.isLocked() || rowIndex < 0 ){
27009 if(e.shiftKey && this.last !== false){
27010 var last = this.last;
27011 this.selectRange(last, rowIndex, e.ctrlKey);
27012 this.last = last; // reset the last
27016 var isSelected = this.isSelected(rowIndex);
27017 //Roo.log("select row:" + rowIndex);
27019 this.deselectRow(rowIndex);
27021 this.selectRow(rowIndex, true);
27025 if(e.button !== 0 && isSelected){
27026 alert('rowIndex 2: ' + rowIndex);
27027 view.focusRow(rowIndex);
27028 }else if(e.ctrlKey && isSelected){
27029 this.deselectRow(rowIndex);
27030 }else if(!isSelected){
27031 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27032 view.focusRow(rowIndex);
27036 this.fireEvent("afterselectionchange", this);
27039 handleDragableRowClick : function(grid, rowIndex, e)
27041 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27042 this.selectRow(rowIndex, false);
27043 grid.view.focusRow(rowIndex);
27044 this.fireEvent("afterselectionchange", this);
27049 * Selects multiple rows.
27050 * @param {Array} rows Array of the indexes of the row to select
27051 * @param {Boolean} keepExisting (optional) True to keep existing selections
27053 selectRows : function(rows, keepExisting){
27055 this.clearSelections();
27057 for(var i = 0, len = rows.length; i < len; i++){
27058 this.selectRow(rows[i], true);
27063 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27064 * @param {Number} startRow The index of the first row in the range
27065 * @param {Number} endRow The index of the last row in the range
27066 * @param {Boolean} keepExisting (optional) True to retain existing selections
27068 selectRange : function(startRow, endRow, keepExisting){
27073 this.clearSelections();
27075 if(startRow <= endRow){
27076 for(var i = startRow; i <= endRow; i++){
27077 this.selectRow(i, true);
27080 for(var i = startRow; i >= endRow; i--){
27081 this.selectRow(i, true);
27087 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27088 * @param {Number} startRow The index of the first row in the range
27089 * @param {Number} endRow The index of the last row in the range
27091 deselectRange : function(startRow, endRow, preventViewNotify){
27095 for(var i = startRow; i <= endRow; i++){
27096 this.deselectRow(i, preventViewNotify);
27102 * @param {Number} row The index of the row to select
27103 * @param {Boolean} keepExisting (optional) True to keep existing selections
27105 selectRow : function(index, keepExisting, preventViewNotify)
27107 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27110 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27111 if(!keepExisting || this.singleSelect){
27112 this.clearSelections();
27115 var r = this.grid.store.getAt(index);
27116 //console.log('selectRow - record id :' + r.id);
27118 this.selections.add(r);
27119 this.last = this.lastActive = index;
27120 if(!preventViewNotify){
27121 var proxy = new Roo.Element(
27122 this.grid.getRowDom(index)
27124 proxy.addClass('bg-info info');
27126 this.fireEvent("rowselect", this, index, r);
27127 this.fireEvent("selectionchange", this);
27133 * @param {Number} row The index of the row to deselect
27135 deselectRow : function(index, preventViewNotify)
27140 if(this.last == index){
27143 if(this.lastActive == index){
27144 this.lastActive = false;
27147 var r = this.grid.store.getAt(index);
27152 this.selections.remove(r);
27153 //.console.log('deselectRow - record id :' + r.id);
27154 if(!preventViewNotify){
27156 var proxy = new Roo.Element(
27157 this.grid.getRowDom(index)
27159 proxy.removeClass('bg-info info');
27161 this.fireEvent("rowdeselect", this, index);
27162 this.fireEvent("selectionchange", this);
27166 restoreLast : function(){
27168 this.last = this._last;
27173 acceptsNav : function(row, col, cm){
27174 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27178 onEditorKey : function(field, e){
27179 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27184 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27186 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27188 }else if(k == e.ENTER && !e.ctrlKey){
27192 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27194 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27196 }else if(k == e.ESC){
27200 g.startEditing(newCell[0], newCell[1]);
27206 * Ext JS Library 1.1.1
27207 * Copyright(c) 2006-2007, Ext JS, LLC.
27209 * Originally Released Under LGPL - original licence link has changed is not relivant.
27212 * <script type="text/javascript">
27216 * @class Roo.bootstrap.PagingToolbar
27217 * @extends Roo.bootstrap.NavSimplebar
27218 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27220 * Create a new PagingToolbar
27221 * @param {Object} config The config object
27222 * @param {Roo.data.Store} store
27224 Roo.bootstrap.PagingToolbar = function(config)
27226 // old args format still supported... - xtype is prefered..
27227 // created from xtype...
27229 this.ds = config.dataSource;
27231 if (config.store && !this.ds) {
27232 this.store= Roo.factory(config.store, Roo.data);
27233 this.ds = this.store;
27234 this.ds.xmodule = this.xmodule || false;
27237 this.toolbarItems = [];
27238 if (config.items) {
27239 this.toolbarItems = config.items;
27242 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27247 this.bind(this.ds);
27250 if (Roo.bootstrap.version == 4) {
27251 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27253 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27258 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27260 * @cfg {Roo.data.Store} dataSource
27261 * The underlying data store providing the paged data
27264 * @cfg {String/HTMLElement/Element} container
27265 * container The id or element that will contain the toolbar
27268 * @cfg {Boolean} displayInfo
27269 * True to display the displayMsg (defaults to false)
27272 * @cfg {Number} pageSize
27273 * The number of records to display per page (defaults to 20)
27277 * @cfg {String} displayMsg
27278 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27280 displayMsg : 'Displaying {0} - {1} of {2}',
27282 * @cfg {String} emptyMsg
27283 * The message to display when no records are found (defaults to "No data to display")
27285 emptyMsg : 'No data to display',
27287 * Customizable piece of the default paging text (defaults to "Page")
27290 beforePageText : "Page",
27292 * Customizable piece of the default paging text (defaults to "of %0")
27295 afterPageText : "of {0}",
27297 * Customizable piece of the default paging text (defaults to "First Page")
27300 firstText : "First Page",
27302 * Customizable piece of the default paging text (defaults to "Previous Page")
27305 prevText : "Previous Page",
27307 * Customizable piece of the default paging text (defaults to "Next Page")
27310 nextText : "Next Page",
27312 * Customizable piece of the default paging text (defaults to "Last Page")
27315 lastText : "Last Page",
27317 * Customizable piece of the default paging text (defaults to "Refresh")
27320 refreshText : "Refresh",
27324 onRender : function(ct, position)
27326 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27327 this.navgroup.parentId = this.id;
27328 this.navgroup.onRender(this.el, null);
27329 // add the buttons to the navgroup
27331 if(this.displayInfo){
27332 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27333 this.displayEl = this.el.select('.x-paging-info', true).first();
27334 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27335 // this.displayEl = navel.el.select('span',true).first();
27341 Roo.each(_this.buttons, function(e){ // this might need to use render????
27342 Roo.factory(e).render(_this.el);
27346 Roo.each(_this.toolbarItems, function(e) {
27347 _this.navgroup.addItem(e);
27351 this.first = this.navgroup.addItem({
27352 tooltip: this.firstText,
27353 cls: "prev btn-outline-secondary",
27354 html : ' <i class="fa fa-step-backward"></i>',
27356 preventDefault: true,
27357 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27360 this.prev = this.navgroup.addItem({
27361 tooltip: this.prevText,
27362 cls: "prev btn-outline-secondary",
27363 html : ' <i class="fa fa-backward"></i>',
27365 preventDefault: true,
27366 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27368 //this.addSeparator();
27371 var field = this.navgroup.addItem( {
27373 cls : 'x-paging-position btn-outline-secondary',
27375 html : this.beforePageText +
27376 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27377 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27380 this.field = field.el.select('input', true).first();
27381 this.field.on("keydown", this.onPagingKeydown, this);
27382 this.field.on("focus", function(){this.dom.select();});
27385 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27386 //this.field.setHeight(18);
27387 //this.addSeparator();
27388 this.next = this.navgroup.addItem({
27389 tooltip: this.nextText,
27390 cls: "next btn-outline-secondary",
27391 html : ' <i class="fa fa-forward"></i>',
27393 preventDefault: true,
27394 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27396 this.last = this.navgroup.addItem({
27397 tooltip: this.lastText,
27398 html : ' <i class="fa fa-step-forward"></i>',
27399 cls: "next btn-outline-secondary",
27401 preventDefault: true,
27402 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27404 //this.addSeparator();
27405 this.loading = this.navgroup.addItem({
27406 tooltip: this.refreshText,
27407 cls: "btn-outline-secondary",
27408 html : ' <i class="fa fa-refresh"></i>',
27409 preventDefault: true,
27410 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27416 updateInfo : function(){
27417 if(this.displayEl){
27418 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27419 var msg = count == 0 ?
27423 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27425 this.displayEl.update(msg);
27430 onLoad : function(ds, r, o)
27432 this.cursor = o.params && o.params.start ? o.params.start : 0;
27434 var d = this.getPageData(),
27439 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27440 this.field.dom.value = ap;
27441 this.first.setDisabled(ap == 1);
27442 this.prev.setDisabled(ap == 1);
27443 this.next.setDisabled(ap == ps);
27444 this.last.setDisabled(ap == ps);
27445 this.loading.enable();
27450 getPageData : function(){
27451 var total = this.ds.getTotalCount();
27454 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27455 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27460 onLoadError : function(){
27461 this.loading.enable();
27465 onPagingKeydown : function(e){
27466 var k = e.getKey();
27467 var d = this.getPageData();
27469 var v = this.field.dom.value, pageNum;
27470 if(!v || isNaN(pageNum = parseInt(v, 10))){
27471 this.field.dom.value = d.activePage;
27474 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27475 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27478 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))
27480 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27481 this.field.dom.value = pageNum;
27482 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27485 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27487 var v = this.field.dom.value, pageNum;
27488 var increment = (e.shiftKey) ? 10 : 1;
27489 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27492 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27493 this.field.dom.value = d.activePage;
27496 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27498 this.field.dom.value = parseInt(v, 10) + increment;
27499 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27500 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27507 beforeLoad : function(){
27509 this.loading.disable();
27514 onClick : function(which){
27523 ds.load({params:{start: 0, limit: this.pageSize}});
27526 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27529 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27532 var total = ds.getTotalCount();
27533 var extra = total % this.pageSize;
27534 var lastStart = extra ? (total - extra) : total-this.pageSize;
27535 ds.load({params:{start: lastStart, limit: this.pageSize}});
27538 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27544 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27545 * @param {Roo.data.Store} store The data store to unbind
27547 unbind : function(ds){
27548 ds.un("beforeload", this.beforeLoad, this);
27549 ds.un("load", this.onLoad, this);
27550 ds.un("loadexception", this.onLoadError, this);
27551 ds.un("remove", this.updateInfo, this);
27552 ds.un("add", this.updateInfo, this);
27553 this.ds = undefined;
27557 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27558 * @param {Roo.data.Store} store The data store to bind
27560 bind : function(ds){
27561 ds.on("beforeload", this.beforeLoad, this);
27562 ds.on("load", this.onLoad, this);
27563 ds.on("loadexception", this.onLoadError, this);
27564 ds.on("remove", this.updateInfo, this);
27565 ds.on("add", this.updateInfo, this);
27576 * @class Roo.bootstrap.MessageBar
27577 * @extends Roo.bootstrap.Component
27578 * Bootstrap MessageBar class
27579 * @cfg {String} html contents of the MessageBar
27580 * @cfg {String} weight (info | success | warning | danger) default info
27581 * @cfg {String} beforeClass insert the bar before the given class
27582 * @cfg {Boolean} closable (true | false) default false
27583 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27586 * Create a new Element
27587 * @param {Object} config The config object
27590 Roo.bootstrap.MessageBar = function(config){
27591 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27594 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27600 beforeClass: 'bootstrap-sticky-wrap',
27602 getAutoCreate : function(){
27606 cls: 'alert alert-dismissable alert-' + this.weight,
27611 html: this.html || ''
27617 cfg.cls += ' alert-messages-fixed';
27631 onRender : function(ct, position)
27633 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27636 var cfg = Roo.apply({}, this.getAutoCreate());
27640 cfg.cls += ' ' + this.cls;
27643 cfg.style = this.style;
27645 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27647 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27650 this.el.select('>button.close').on('click', this.hide, this);
27656 if (!this.rendered) {
27662 this.fireEvent('show', this);
27668 if (!this.rendered) {
27674 this.fireEvent('hide', this);
27677 update : function()
27679 // var e = this.el.dom.firstChild;
27681 // if(this.closable){
27682 // e = e.nextSibling;
27685 // e.data = this.html || '';
27687 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27703 * @class Roo.bootstrap.Graph
27704 * @extends Roo.bootstrap.Component
27705 * Bootstrap Graph class
27709 @cfg {String} graphtype bar | vbar | pie
27710 @cfg {number} g_x coodinator | centre x (pie)
27711 @cfg {number} g_y coodinator | centre y (pie)
27712 @cfg {number} g_r radius (pie)
27713 @cfg {number} g_height height of the chart (respected by all elements in the set)
27714 @cfg {number} g_width width of the chart (respected by all elements in the set)
27715 @cfg {Object} title The title of the chart
27718 -opts (object) options for the chart
27720 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27721 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27723 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.
27724 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27726 o stretch (boolean)
27728 -opts (object) options for the pie
27731 o startAngle (number)
27732 o endAngle (number)
27736 * Create a new Input
27737 * @param {Object} config The config object
27740 Roo.bootstrap.Graph = function(config){
27741 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27747 * The img click event for the img.
27748 * @param {Roo.EventObject} e
27754 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27765 //g_colors: this.colors,
27772 getAutoCreate : function(){
27783 onRender : function(ct,position){
27786 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27788 if (typeof(Raphael) == 'undefined') {
27789 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27793 this.raphael = Raphael(this.el.dom);
27795 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27796 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27797 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27798 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27800 r.text(160, 10, "Single Series Chart").attr(txtattr);
27801 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27802 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27803 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27805 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27806 r.barchart(330, 10, 300, 220, data1);
27807 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27808 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27811 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27812 // r.barchart(30, 30, 560, 250, xdata, {
27813 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27814 // axis : "0 0 1 1",
27815 // axisxlabels : xdata
27816 // //yvalues : cols,
27819 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27821 // this.load(null,xdata,{
27822 // axis : "0 0 1 1",
27823 // axisxlabels : xdata
27828 load : function(graphtype,xdata,opts)
27830 this.raphael.clear();
27832 graphtype = this.graphtype;
27837 var r = this.raphael,
27838 fin = function () {
27839 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27841 fout = function () {
27842 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27844 pfin = function() {
27845 this.sector.stop();
27846 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27849 this.label[0].stop();
27850 this.label[0].attr({ r: 7.5 });
27851 this.label[1].attr({ "font-weight": 800 });
27854 pfout = function() {
27855 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27858 this.label[0].animate({ r: 5 }, 500, "bounce");
27859 this.label[1].attr({ "font-weight": 400 });
27865 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27868 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27871 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27872 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27874 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27881 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27886 setTitle: function(o)
27891 initEvents: function() {
27894 this.el.on('click', this.onClick, this);
27898 onClick : function(e)
27900 Roo.log('img onclick');
27901 this.fireEvent('click', this, e);
27913 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27916 * @class Roo.bootstrap.dash.NumberBox
27917 * @extends Roo.bootstrap.Component
27918 * Bootstrap NumberBox class
27919 * @cfg {String} headline Box headline
27920 * @cfg {String} content Box content
27921 * @cfg {String} icon Box icon
27922 * @cfg {String} footer Footer text
27923 * @cfg {String} fhref Footer href
27926 * Create a new NumberBox
27927 * @param {Object} config The config object
27931 Roo.bootstrap.dash.NumberBox = function(config){
27932 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27936 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27945 getAutoCreate : function(){
27949 cls : 'small-box ',
27957 cls : 'roo-headline',
27958 html : this.headline
27962 cls : 'roo-content',
27963 html : this.content
27977 cls : 'ion ' + this.icon
27986 cls : 'small-box-footer',
27987 href : this.fhref || '#',
27991 cfg.cn.push(footer);
27998 onRender : function(ct,position){
27999 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28006 setHeadline: function (value)
28008 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28011 setFooter: function (value, href)
28013 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28016 this.el.select('a.small-box-footer',true).first().attr('href', href);
28021 setContent: function (value)
28023 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28026 initEvents: function()
28040 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28043 * @class Roo.bootstrap.dash.TabBox
28044 * @extends Roo.bootstrap.Component
28045 * Bootstrap TabBox class
28046 * @cfg {String} title Title of the TabBox
28047 * @cfg {String} icon Icon of the TabBox
28048 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28049 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28052 * Create a new TabBox
28053 * @param {Object} config The config object
28057 Roo.bootstrap.dash.TabBox = function(config){
28058 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28063 * When a pane is added
28064 * @param {Roo.bootstrap.dash.TabPane} pane
28068 * @event activatepane
28069 * When a pane is activated
28070 * @param {Roo.bootstrap.dash.TabPane} pane
28072 "activatepane" : true
28080 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28085 tabScrollable : false,
28087 getChildContainer : function()
28089 return this.el.select('.tab-content', true).first();
28092 getAutoCreate : function(){
28096 cls: 'pull-left header',
28104 cls: 'fa ' + this.icon
28110 cls: 'nav nav-tabs pull-right',
28116 if(this.tabScrollable){
28123 cls: 'nav nav-tabs pull-right',
28134 cls: 'nav-tabs-custom',
28139 cls: 'tab-content no-padding',
28147 initEvents : function()
28149 //Roo.log('add add pane handler');
28150 this.on('addpane', this.onAddPane, this);
28153 * Updates the box title
28154 * @param {String} html to set the title to.
28156 setTitle : function(value)
28158 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28160 onAddPane : function(pane)
28162 this.panes.push(pane);
28163 //Roo.log('addpane');
28165 // tabs are rendere left to right..
28166 if(!this.showtabs){
28170 var ctr = this.el.select('.nav-tabs', true).first();
28173 var existing = ctr.select('.nav-tab',true);
28174 var qty = existing.getCount();;
28177 var tab = ctr.createChild({
28179 cls : 'nav-tab' + (qty ? '' : ' active'),
28187 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28190 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28192 pane.el.addClass('active');
28197 onTabClick : function(ev,un,ob,pane)
28199 //Roo.log('tab - prev default');
28200 ev.preventDefault();
28203 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28204 pane.tab.addClass('active');
28205 //Roo.log(pane.title);
28206 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28207 // technically we should have a deactivate event.. but maybe add later.
28208 // and it should not de-activate the selected tab...
28209 this.fireEvent('activatepane', pane);
28210 pane.el.addClass('active');
28211 pane.fireEvent('activate');
28216 getActivePane : function()
28219 Roo.each(this.panes, function(p) {
28220 if(p.el.hasClass('active')){
28241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28243 * @class Roo.bootstrap.TabPane
28244 * @extends Roo.bootstrap.Component
28245 * Bootstrap TabPane class
28246 * @cfg {Boolean} active (false | true) Default false
28247 * @cfg {String} title title of panel
28251 * Create a new TabPane
28252 * @param {Object} config The config object
28255 Roo.bootstrap.dash.TabPane = function(config){
28256 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28262 * When a pane is activated
28263 * @param {Roo.bootstrap.dash.TabPane} pane
28270 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28275 // the tabBox that this is attached to.
28278 getAutoCreate : function()
28286 cfg.cls += ' active';
28291 initEvents : function()
28293 //Roo.log('trigger add pane handler');
28294 this.parent().fireEvent('addpane', this)
28298 * Updates the tab title
28299 * @param {String} html to set the title to.
28301 setTitle: function(str)
28307 this.tab.select('a', true).first().dom.innerHTML = str;
28324 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28327 * @class Roo.bootstrap.menu.Menu
28328 * @extends Roo.bootstrap.Component
28329 * Bootstrap Menu class - container for Menu
28330 * @cfg {String} html Text of the menu
28331 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28332 * @cfg {String} icon Font awesome icon
28333 * @cfg {String} pos Menu align to (top | bottom) default bottom
28337 * Create a new Menu
28338 * @param {Object} config The config object
28342 Roo.bootstrap.menu.Menu = function(config){
28343 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28347 * @event beforeshow
28348 * Fires before this menu is displayed
28349 * @param {Roo.bootstrap.menu.Menu} this
28353 * @event beforehide
28354 * Fires before this menu is hidden
28355 * @param {Roo.bootstrap.menu.Menu} this
28360 * Fires after this menu is displayed
28361 * @param {Roo.bootstrap.menu.Menu} this
28366 * Fires after this menu is hidden
28367 * @param {Roo.bootstrap.menu.Menu} this
28372 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28373 * @param {Roo.bootstrap.menu.Menu} this
28374 * @param {Roo.EventObject} e
28381 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28385 weight : 'default',
28390 getChildContainer : function() {
28391 if(this.isSubMenu){
28395 return this.el.select('ul.dropdown-menu', true).first();
28398 getAutoCreate : function()
28403 cls : 'roo-menu-text',
28411 cls : 'fa ' + this.icon
28422 cls : 'dropdown-button btn btn-' + this.weight,
28427 cls : 'dropdown-toggle btn btn-' + this.weight,
28437 cls : 'dropdown-menu'
28443 if(this.pos == 'top'){
28444 cfg.cls += ' dropup';
28447 if(this.isSubMenu){
28450 cls : 'dropdown-menu'
28457 onRender : function(ct, position)
28459 this.isSubMenu = ct.hasClass('dropdown-submenu');
28461 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28464 initEvents : function()
28466 if(this.isSubMenu){
28470 this.hidden = true;
28472 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28473 this.triggerEl.on('click', this.onTriggerPress, this);
28475 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28476 this.buttonEl.on('click', this.onClick, this);
28482 if(this.isSubMenu){
28486 return this.el.select('ul.dropdown-menu', true).first();
28489 onClick : function(e)
28491 this.fireEvent("click", this, e);
28494 onTriggerPress : function(e)
28496 if (this.isVisible()) {
28503 isVisible : function(){
28504 return !this.hidden;
28509 this.fireEvent("beforeshow", this);
28511 this.hidden = false;
28512 this.el.addClass('open');
28514 Roo.get(document).on("mouseup", this.onMouseUp, this);
28516 this.fireEvent("show", this);
28523 this.fireEvent("beforehide", this);
28525 this.hidden = true;
28526 this.el.removeClass('open');
28528 Roo.get(document).un("mouseup", this.onMouseUp);
28530 this.fireEvent("hide", this);
28533 onMouseUp : function()
28547 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28550 * @class Roo.bootstrap.menu.Item
28551 * @extends Roo.bootstrap.Component
28552 * Bootstrap MenuItem class
28553 * @cfg {Boolean} submenu (true | false) default false
28554 * @cfg {String} html text of the item
28555 * @cfg {String} href the link
28556 * @cfg {Boolean} disable (true | false) default false
28557 * @cfg {Boolean} preventDefault (true | false) default true
28558 * @cfg {String} icon Font awesome icon
28559 * @cfg {String} pos Submenu align to (left | right) default right
28563 * Create a new Item
28564 * @param {Object} config The config object
28568 Roo.bootstrap.menu.Item = function(config){
28569 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28573 * Fires when the mouse is hovering over this menu
28574 * @param {Roo.bootstrap.menu.Item} this
28575 * @param {Roo.EventObject} e
28580 * Fires when the mouse exits this menu
28581 * @param {Roo.bootstrap.menu.Item} this
28582 * @param {Roo.EventObject} e
28588 * The raw click event for the entire grid.
28589 * @param {Roo.EventObject} e
28595 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28600 preventDefault: true,
28605 getAutoCreate : function()
28610 cls : 'roo-menu-item-text',
28618 cls : 'fa ' + this.icon
28627 href : this.href || '#',
28634 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28638 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28640 if(this.pos == 'left'){
28641 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28648 initEvents : function()
28650 this.el.on('mouseover', this.onMouseOver, this);
28651 this.el.on('mouseout', this.onMouseOut, this);
28653 this.el.select('a', true).first().on('click', this.onClick, this);
28657 onClick : function(e)
28659 if(this.preventDefault){
28660 e.preventDefault();
28663 this.fireEvent("click", this, e);
28666 onMouseOver : function(e)
28668 if(this.submenu && this.pos == 'left'){
28669 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28672 this.fireEvent("mouseover", this, e);
28675 onMouseOut : function(e)
28677 this.fireEvent("mouseout", this, e);
28689 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28692 * @class Roo.bootstrap.menu.Separator
28693 * @extends Roo.bootstrap.Component
28694 * Bootstrap Separator class
28697 * Create a new Separator
28698 * @param {Object} config The config object
28702 Roo.bootstrap.menu.Separator = function(config){
28703 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28706 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28708 getAutoCreate : function(){
28729 * @class Roo.bootstrap.Tooltip
28730 * Bootstrap Tooltip class
28731 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28732 * to determine which dom element triggers the tooltip.
28734 * It needs to add support for additional attributes like tooltip-position
28737 * Create a new Toolti
28738 * @param {Object} config The config object
28741 Roo.bootstrap.Tooltip = function(config){
28742 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28744 this.alignment = Roo.bootstrap.Tooltip.alignment;
28746 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28747 this.alignment = config.alignment;
28752 Roo.apply(Roo.bootstrap.Tooltip, {
28754 * @function init initialize tooltip monitoring.
28758 currentTip : false,
28759 currentRegion : false,
28765 Roo.get(document).on('mouseover', this.enter ,this);
28766 Roo.get(document).on('mouseout', this.leave, this);
28769 this.currentTip = new Roo.bootstrap.Tooltip();
28772 enter : function(ev)
28774 var dom = ev.getTarget();
28776 //Roo.log(['enter',dom]);
28777 var el = Roo.fly(dom);
28778 if (this.currentEl) {
28780 //Roo.log(this.currentEl);
28781 //Roo.log(this.currentEl.contains(dom));
28782 if (this.currentEl == el) {
28785 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28791 if (this.currentTip.el) {
28792 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28796 if(!el || el.dom == document){
28802 // you can not look for children, as if el is the body.. then everythign is the child..
28803 if (!el.attr('tooltip')) { //
28804 if (!el.select("[tooltip]").elements.length) {
28807 // is the mouse over this child...?
28808 bindEl = el.select("[tooltip]").first();
28809 var xy = ev.getXY();
28810 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28811 //Roo.log("not in region.");
28814 //Roo.log("child element over..");
28817 this.currentEl = bindEl;
28818 this.currentTip.bind(bindEl);
28819 this.currentRegion = Roo.lib.Region.getRegion(dom);
28820 this.currentTip.enter();
28823 leave : function(ev)
28825 var dom = ev.getTarget();
28826 //Roo.log(['leave',dom]);
28827 if (!this.currentEl) {
28832 if (dom != this.currentEl.dom) {
28835 var xy = ev.getXY();
28836 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28839 // only activate leave if mouse cursor is outside... bounding box..
28844 if (this.currentTip) {
28845 this.currentTip.leave();
28847 //Roo.log('clear currentEl');
28848 this.currentEl = false;
28853 'left' : ['r-l', [-2,0], 'right'],
28854 'right' : ['l-r', [2,0], 'left'],
28855 'bottom' : ['t-b', [0,2], 'top'],
28856 'top' : [ 'b-t', [0,-2], 'bottom']
28862 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28867 delay : null, // can be { show : 300 , hide: 500}
28871 hoverState : null, //???
28873 placement : 'bottom',
28877 getAutoCreate : function(){
28884 cls : 'tooltip-arrow arrow'
28887 cls : 'tooltip-inner'
28894 bind : function(el)
28899 initEvents : function()
28901 this.arrowEl = this.el.select('.arrow', true).first();
28902 this.innerEl = this.el.select('.tooltip-inner', true).first();
28905 enter : function () {
28907 if (this.timeout != null) {
28908 clearTimeout(this.timeout);
28911 this.hoverState = 'in';
28912 //Roo.log("enter - show");
28913 if (!this.delay || !this.delay.show) {
28918 this.timeout = setTimeout(function () {
28919 if (_t.hoverState == 'in') {
28922 }, this.delay.show);
28926 clearTimeout(this.timeout);
28928 this.hoverState = 'out';
28929 if (!this.delay || !this.delay.hide) {
28935 this.timeout = setTimeout(function () {
28936 //Roo.log("leave - timeout");
28938 if (_t.hoverState == 'out') {
28940 Roo.bootstrap.Tooltip.currentEl = false;
28945 show : function (msg)
28948 this.render(document.body);
28951 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28953 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28955 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28957 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28958 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28960 var placement = typeof this.placement == 'function' ?
28961 this.placement.call(this, this.el, on_el) :
28964 var autoToken = /\s?auto?\s?/i;
28965 var autoPlace = autoToken.test(placement);
28967 placement = placement.replace(autoToken, '') || 'top';
28971 //this.el.setXY([0,0]);
28973 //this.el.dom.style.display='block';
28975 //this.el.appendTo(on_el);
28977 var p = this.getPosition();
28978 var box = this.el.getBox();
28984 var align = this.alignment[placement];
28986 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28988 if(placement == 'top' || placement == 'bottom'){
28990 placement = 'right';
28993 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28994 placement = 'left';
28997 var scroll = Roo.select('body', true).first().getScroll();
28999 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29003 align = this.alignment[placement];
29005 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29009 this.el.alignTo(this.bindEl, align[0],align[1]);
29010 //var arrow = this.el.select('.arrow',true).first();
29011 //arrow.set(align[2],
29013 this.el.addClass(placement);
29014 this.el.addClass("bs-tooltip-"+ placement);
29016 this.el.addClass('in fade show');
29018 this.hoverState = null;
29020 if (this.el.hasClass('fade')) {
29035 //this.el.setXY([0,0]);
29036 this.el.removeClass(['show', 'in']);
29052 * @class Roo.bootstrap.LocationPicker
29053 * @extends Roo.bootstrap.Component
29054 * Bootstrap LocationPicker class
29055 * @cfg {Number} latitude Position when init default 0
29056 * @cfg {Number} longitude Position when init default 0
29057 * @cfg {Number} zoom default 15
29058 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29059 * @cfg {Boolean} mapTypeControl default false
29060 * @cfg {Boolean} disableDoubleClickZoom default false
29061 * @cfg {Boolean} scrollwheel default true
29062 * @cfg {Boolean} streetViewControl default false
29063 * @cfg {Number} radius default 0
29064 * @cfg {String} locationName
29065 * @cfg {Boolean} draggable default true
29066 * @cfg {Boolean} enableAutocomplete default false
29067 * @cfg {Boolean} enableReverseGeocode default true
29068 * @cfg {String} markerTitle
29071 * Create a new LocationPicker
29072 * @param {Object} config The config object
29076 Roo.bootstrap.LocationPicker = function(config){
29078 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29083 * Fires when the picker initialized.
29084 * @param {Roo.bootstrap.LocationPicker} this
29085 * @param {Google Location} location
29089 * @event positionchanged
29090 * Fires when the picker position changed.
29091 * @param {Roo.bootstrap.LocationPicker} this
29092 * @param {Google Location} location
29094 positionchanged : true,
29097 * Fires when the map resize.
29098 * @param {Roo.bootstrap.LocationPicker} this
29103 * Fires when the map show.
29104 * @param {Roo.bootstrap.LocationPicker} this
29109 * Fires when the map hide.
29110 * @param {Roo.bootstrap.LocationPicker} this
29115 * Fires when click the map.
29116 * @param {Roo.bootstrap.LocationPicker} this
29117 * @param {Map event} e
29121 * @event mapRightClick
29122 * Fires when right click the map.
29123 * @param {Roo.bootstrap.LocationPicker} this
29124 * @param {Map event} e
29126 mapRightClick : true,
29128 * @event markerClick
29129 * Fires when click the marker.
29130 * @param {Roo.bootstrap.LocationPicker} this
29131 * @param {Map event} e
29133 markerClick : true,
29135 * @event markerRightClick
29136 * Fires when right click the marker.
29137 * @param {Roo.bootstrap.LocationPicker} this
29138 * @param {Map event} e
29140 markerRightClick : true,
29142 * @event OverlayViewDraw
29143 * Fires when OverlayView Draw
29144 * @param {Roo.bootstrap.LocationPicker} this
29146 OverlayViewDraw : true,
29148 * @event OverlayViewOnAdd
29149 * Fires when OverlayView Draw
29150 * @param {Roo.bootstrap.LocationPicker} this
29152 OverlayViewOnAdd : true,
29154 * @event OverlayViewOnRemove
29155 * Fires when OverlayView Draw
29156 * @param {Roo.bootstrap.LocationPicker} this
29158 OverlayViewOnRemove : true,
29160 * @event OverlayViewShow
29161 * Fires when OverlayView Draw
29162 * @param {Roo.bootstrap.LocationPicker} this
29163 * @param {Pixel} cpx
29165 OverlayViewShow : true,
29167 * @event OverlayViewHide
29168 * Fires when OverlayView Draw
29169 * @param {Roo.bootstrap.LocationPicker} this
29171 OverlayViewHide : true,
29173 * @event loadexception
29174 * Fires when load google lib failed.
29175 * @param {Roo.bootstrap.LocationPicker} this
29177 loadexception : true
29182 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29184 gMapContext: false,
29190 mapTypeControl: false,
29191 disableDoubleClickZoom: false,
29193 streetViewControl: false,
29197 enableAutocomplete: false,
29198 enableReverseGeocode: true,
29201 getAutoCreate: function()
29206 cls: 'roo-location-picker'
29212 initEvents: function(ct, position)
29214 if(!this.el.getWidth() || this.isApplied()){
29218 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29223 initial: function()
29225 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29226 this.fireEvent('loadexception', this);
29230 if(!this.mapTypeId){
29231 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29234 this.gMapContext = this.GMapContext();
29236 this.initOverlayView();
29238 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29242 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29243 _this.setPosition(_this.gMapContext.marker.position);
29246 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29247 _this.fireEvent('mapClick', this, event);
29251 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29252 _this.fireEvent('mapRightClick', this, event);
29256 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29257 _this.fireEvent('markerClick', this, event);
29261 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29262 _this.fireEvent('markerRightClick', this, event);
29266 this.setPosition(this.gMapContext.location);
29268 this.fireEvent('initial', this, this.gMapContext.location);
29271 initOverlayView: function()
29275 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29279 _this.fireEvent('OverlayViewDraw', _this);
29284 _this.fireEvent('OverlayViewOnAdd', _this);
29287 onRemove: function()
29289 _this.fireEvent('OverlayViewOnRemove', _this);
29292 show: function(cpx)
29294 _this.fireEvent('OverlayViewShow', _this, cpx);
29299 _this.fireEvent('OverlayViewHide', _this);
29305 fromLatLngToContainerPixel: function(event)
29307 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29310 isApplied: function()
29312 return this.getGmapContext() == false ? false : true;
29315 getGmapContext: function()
29317 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29320 GMapContext: function()
29322 var position = new google.maps.LatLng(this.latitude, this.longitude);
29324 var _map = new google.maps.Map(this.el.dom, {
29327 mapTypeId: this.mapTypeId,
29328 mapTypeControl: this.mapTypeControl,
29329 disableDoubleClickZoom: this.disableDoubleClickZoom,
29330 scrollwheel: this.scrollwheel,
29331 streetViewControl: this.streetViewControl,
29332 locationName: this.locationName,
29333 draggable: this.draggable,
29334 enableAutocomplete: this.enableAutocomplete,
29335 enableReverseGeocode: this.enableReverseGeocode
29338 var _marker = new google.maps.Marker({
29339 position: position,
29341 title: this.markerTitle,
29342 draggable: this.draggable
29349 location: position,
29350 radius: this.radius,
29351 locationName: this.locationName,
29352 addressComponents: {
29353 formatted_address: null,
29354 addressLine1: null,
29355 addressLine2: null,
29357 streetNumber: null,
29361 stateOrProvince: null
29364 domContainer: this.el.dom,
29365 geodecoder: new google.maps.Geocoder()
29369 drawCircle: function(center, radius, options)
29371 if (this.gMapContext.circle != null) {
29372 this.gMapContext.circle.setMap(null);
29376 options = Roo.apply({}, options, {
29377 strokeColor: "#0000FF",
29378 strokeOpacity: .35,
29380 fillColor: "#0000FF",
29384 options.map = this.gMapContext.map;
29385 options.radius = radius;
29386 options.center = center;
29387 this.gMapContext.circle = new google.maps.Circle(options);
29388 return this.gMapContext.circle;
29394 setPosition: function(location)
29396 this.gMapContext.location = location;
29397 this.gMapContext.marker.setPosition(location);
29398 this.gMapContext.map.panTo(location);
29399 this.drawCircle(location, this.gMapContext.radius, {});
29403 if (this.gMapContext.settings.enableReverseGeocode) {
29404 this.gMapContext.geodecoder.geocode({
29405 latLng: this.gMapContext.location
29406 }, function(results, status) {
29408 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29409 _this.gMapContext.locationName = results[0].formatted_address;
29410 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29412 _this.fireEvent('positionchanged', this, location);
29419 this.fireEvent('positionchanged', this, location);
29424 google.maps.event.trigger(this.gMapContext.map, "resize");
29426 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29428 this.fireEvent('resize', this);
29431 setPositionByLatLng: function(latitude, longitude)
29433 this.setPosition(new google.maps.LatLng(latitude, longitude));
29436 getCurrentPosition: function()
29439 latitude: this.gMapContext.location.lat(),
29440 longitude: this.gMapContext.location.lng()
29444 getAddressName: function()
29446 return this.gMapContext.locationName;
29449 getAddressComponents: function()
29451 return this.gMapContext.addressComponents;
29454 address_component_from_google_geocode: function(address_components)
29458 for (var i = 0; i < address_components.length; i++) {
29459 var component = address_components[i];
29460 if (component.types.indexOf("postal_code") >= 0) {
29461 result.postalCode = component.short_name;
29462 } else if (component.types.indexOf("street_number") >= 0) {
29463 result.streetNumber = component.short_name;
29464 } else if (component.types.indexOf("route") >= 0) {
29465 result.streetName = component.short_name;
29466 } else if (component.types.indexOf("neighborhood") >= 0) {
29467 result.city = component.short_name;
29468 } else if (component.types.indexOf("locality") >= 0) {
29469 result.city = component.short_name;
29470 } else if (component.types.indexOf("sublocality") >= 0) {
29471 result.district = component.short_name;
29472 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29473 result.stateOrProvince = component.short_name;
29474 } else if (component.types.indexOf("country") >= 0) {
29475 result.country = component.short_name;
29479 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29480 result.addressLine2 = "";
29484 setZoomLevel: function(zoom)
29486 this.gMapContext.map.setZoom(zoom);
29499 this.fireEvent('show', this);
29510 this.fireEvent('hide', this);
29515 Roo.apply(Roo.bootstrap.LocationPicker, {
29517 OverlayView : function(map, options)
29519 options = options || {};
29526 * @class Roo.bootstrap.Alert
29527 * @extends Roo.bootstrap.Component
29528 * Bootstrap Alert class - shows an alert area box
29530 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29531 Enter a valid email address
29534 * @cfg {String} title The title of alert
29535 * @cfg {String} html The content of alert
29536 * @cfg {String} weight ( success | info | warning | danger )
29537 * @cfg {String} faicon font-awesomeicon
29540 * Create a new alert
29541 * @param {Object} config The config object
29545 Roo.bootstrap.Alert = function(config){
29546 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29550 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29557 getAutoCreate : function()
29566 cls : 'roo-alert-icon'
29571 cls : 'roo-alert-title',
29576 cls : 'roo-alert-text',
29583 cfg.cn[0].cls += ' fa ' + this.faicon;
29587 cfg.cls += ' alert-' + this.weight;
29593 initEvents: function()
29595 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29598 setTitle : function(str)
29600 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29603 setText : function(str)
29605 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29608 setWeight : function(weight)
29611 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29614 this.weight = weight;
29616 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29619 setIcon : function(icon)
29622 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29625 this.faicon = icon;
29627 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29648 * @class Roo.bootstrap.UploadCropbox
29649 * @extends Roo.bootstrap.Component
29650 * Bootstrap UploadCropbox class
29651 * @cfg {String} emptyText show when image has been loaded
29652 * @cfg {String} rotateNotify show when image too small to rotate
29653 * @cfg {Number} errorTimeout default 3000
29654 * @cfg {Number} minWidth default 300
29655 * @cfg {Number} minHeight default 300
29656 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29657 * @cfg {Boolean} isDocument (true|false) default false
29658 * @cfg {String} url action url
29659 * @cfg {String} paramName default 'imageUpload'
29660 * @cfg {String} method default POST
29661 * @cfg {Boolean} loadMask (true|false) default true
29662 * @cfg {Boolean} loadingText default 'Loading...'
29665 * Create a new UploadCropbox
29666 * @param {Object} config The config object
29669 Roo.bootstrap.UploadCropbox = function(config){
29670 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29674 * @event beforeselectfile
29675 * Fire before select file
29676 * @param {Roo.bootstrap.UploadCropbox} this
29678 "beforeselectfile" : true,
29681 * Fire after initEvent
29682 * @param {Roo.bootstrap.UploadCropbox} this
29687 * Fire after initEvent
29688 * @param {Roo.bootstrap.UploadCropbox} this
29689 * @param {String} data
29694 * Fire when preparing the file data
29695 * @param {Roo.bootstrap.UploadCropbox} this
29696 * @param {Object} file
29701 * Fire when get exception
29702 * @param {Roo.bootstrap.UploadCropbox} this
29703 * @param {XMLHttpRequest} xhr
29705 "exception" : true,
29707 * @event beforeloadcanvas
29708 * Fire before load the canvas
29709 * @param {Roo.bootstrap.UploadCropbox} this
29710 * @param {String} src
29712 "beforeloadcanvas" : true,
29715 * Fire when trash image
29716 * @param {Roo.bootstrap.UploadCropbox} this
29721 * Fire when download the image
29722 * @param {Roo.bootstrap.UploadCropbox} this
29726 * @event footerbuttonclick
29727 * Fire when footerbuttonclick
29728 * @param {Roo.bootstrap.UploadCropbox} this
29729 * @param {String} type
29731 "footerbuttonclick" : true,
29735 * @param {Roo.bootstrap.UploadCropbox} this
29740 * Fire when rotate the image
29741 * @param {Roo.bootstrap.UploadCropbox} this
29742 * @param {String} pos
29747 * Fire when inspect the file
29748 * @param {Roo.bootstrap.UploadCropbox} this
29749 * @param {Object} file
29754 * Fire when xhr upload the file
29755 * @param {Roo.bootstrap.UploadCropbox} this
29756 * @param {Object} data
29761 * Fire when arrange the file data
29762 * @param {Roo.bootstrap.UploadCropbox} this
29763 * @param {Object} formData
29768 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29771 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29773 emptyText : 'Click to upload image',
29774 rotateNotify : 'Image is too small to rotate',
29775 errorTimeout : 3000,
29789 cropType : 'image/jpeg',
29791 canvasLoaded : false,
29792 isDocument : false,
29794 paramName : 'imageUpload',
29796 loadingText : 'Loading...',
29799 getAutoCreate : function()
29803 cls : 'roo-upload-cropbox',
29807 cls : 'roo-upload-cropbox-selector',
29812 cls : 'roo-upload-cropbox-body',
29813 style : 'cursor:pointer',
29817 cls : 'roo-upload-cropbox-preview'
29821 cls : 'roo-upload-cropbox-thumb'
29825 cls : 'roo-upload-cropbox-empty-notify',
29826 html : this.emptyText
29830 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29831 html : this.rotateNotify
29837 cls : 'roo-upload-cropbox-footer',
29840 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29850 onRender : function(ct, position)
29852 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29854 if (this.buttons.length) {
29856 Roo.each(this.buttons, function(bb) {
29858 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29860 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29866 this.maskEl = this.el;
29870 initEvents : function()
29872 this.urlAPI = (window.createObjectURL && window) ||
29873 (window.URL && URL.revokeObjectURL && URL) ||
29874 (window.webkitURL && webkitURL);
29876 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29877 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29879 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29880 this.selectorEl.hide();
29882 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29883 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29885 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29886 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29887 this.thumbEl.hide();
29889 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29890 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29892 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29893 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29894 this.errorEl.hide();
29896 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29897 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29898 this.footerEl.hide();
29900 this.setThumbBoxSize();
29906 this.fireEvent('initial', this);
29913 window.addEventListener("resize", function() { _this.resize(); } );
29915 this.bodyEl.on('click', this.beforeSelectFile, this);
29918 this.bodyEl.on('touchstart', this.onTouchStart, this);
29919 this.bodyEl.on('touchmove', this.onTouchMove, this);
29920 this.bodyEl.on('touchend', this.onTouchEnd, this);
29924 this.bodyEl.on('mousedown', this.onMouseDown, this);
29925 this.bodyEl.on('mousemove', this.onMouseMove, this);
29926 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29927 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29928 Roo.get(document).on('mouseup', this.onMouseUp, this);
29931 this.selectorEl.on('change', this.onFileSelected, this);
29937 this.baseScale = 1;
29939 this.baseRotate = 1;
29940 this.dragable = false;
29941 this.pinching = false;
29944 this.cropData = false;
29945 this.notifyEl.dom.innerHTML = this.emptyText;
29947 this.selectorEl.dom.value = '';
29951 resize : function()
29953 if(this.fireEvent('resize', this) != false){
29954 this.setThumbBoxPosition();
29955 this.setCanvasPosition();
29959 onFooterButtonClick : function(e, el, o, type)
29962 case 'rotate-left' :
29963 this.onRotateLeft(e);
29965 case 'rotate-right' :
29966 this.onRotateRight(e);
29969 this.beforeSelectFile(e);
29984 this.fireEvent('footerbuttonclick', this, type);
29987 beforeSelectFile : function(e)
29989 e.preventDefault();
29991 if(this.fireEvent('beforeselectfile', this) != false){
29992 this.selectorEl.dom.click();
29996 onFileSelected : function(e)
29998 e.preventDefault();
30000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30004 var file = this.selectorEl.dom.files[0];
30006 if(this.fireEvent('inspect', this, file) != false){
30007 this.prepare(file);
30012 trash : function(e)
30014 this.fireEvent('trash', this);
30017 download : function(e)
30019 this.fireEvent('download', this);
30022 loadCanvas : function(src)
30024 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30028 this.imageEl = document.createElement('img');
30032 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30034 this.imageEl.src = src;
30038 onLoadCanvas : function()
30040 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30041 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30043 this.bodyEl.un('click', this.beforeSelectFile, this);
30045 this.notifyEl.hide();
30046 this.thumbEl.show();
30047 this.footerEl.show();
30049 this.baseRotateLevel();
30051 if(this.isDocument){
30052 this.setThumbBoxSize();
30055 this.setThumbBoxPosition();
30057 this.baseScaleLevel();
30063 this.canvasLoaded = true;
30066 this.maskEl.unmask();
30071 setCanvasPosition : function()
30073 if(!this.canvasEl){
30077 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30078 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30080 this.previewEl.setLeft(pw);
30081 this.previewEl.setTop(ph);
30085 onMouseDown : function(e)
30089 this.dragable = true;
30090 this.pinching = false;
30092 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30093 this.dragable = false;
30097 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30098 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30102 onMouseMove : function(e)
30106 if(!this.canvasLoaded){
30110 if (!this.dragable){
30114 var minX = Math.ceil(this.thumbEl.getLeft(true));
30115 var minY = Math.ceil(this.thumbEl.getTop(true));
30117 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30118 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30120 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30121 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30123 x = x - this.mouseX;
30124 y = y - this.mouseY;
30126 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30127 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30129 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30130 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30132 this.previewEl.setLeft(bgX);
30133 this.previewEl.setTop(bgY);
30135 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30136 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30139 onMouseUp : function(e)
30143 this.dragable = false;
30146 onMouseWheel : function(e)
30150 this.startScale = this.scale;
30152 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30154 if(!this.zoomable()){
30155 this.scale = this.startScale;
30164 zoomable : function()
30166 var minScale = this.thumbEl.getWidth() / this.minWidth;
30168 if(this.minWidth < this.minHeight){
30169 minScale = this.thumbEl.getHeight() / this.minHeight;
30172 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30173 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30177 (this.rotate == 0 || this.rotate == 180) &&
30179 width > this.imageEl.OriginWidth ||
30180 height > this.imageEl.OriginHeight ||
30181 (width < this.minWidth && height < this.minHeight)
30189 (this.rotate == 90 || this.rotate == 270) &&
30191 width > this.imageEl.OriginWidth ||
30192 height > this.imageEl.OriginHeight ||
30193 (width < this.minHeight && height < this.minWidth)
30200 !this.isDocument &&
30201 (this.rotate == 0 || this.rotate == 180) &&
30203 width < this.minWidth ||
30204 width > this.imageEl.OriginWidth ||
30205 height < this.minHeight ||
30206 height > this.imageEl.OriginHeight
30213 !this.isDocument &&
30214 (this.rotate == 90 || this.rotate == 270) &&
30216 width < this.minHeight ||
30217 width > this.imageEl.OriginWidth ||
30218 height < this.minWidth ||
30219 height > this.imageEl.OriginHeight
30229 onRotateLeft : function(e)
30231 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30233 var minScale = this.thumbEl.getWidth() / this.minWidth;
30235 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30236 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30238 this.startScale = this.scale;
30240 while (this.getScaleLevel() < minScale){
30242 this.scale = this.scale + 1;
30244 if(!this.zoomable()){
30249 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30250 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30255 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30262 this.scale = this.startScale;
30264 this.onRotateFail();
30269 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30271 if(this.isDocument){
30272 this.setThumbBoxSize();
30273 this.setThumbBoxPosition();
30274 this.setCanvasPosition();
30279 this.fireEvent('rotate', this, 'left');
30283 onRotateRight : function(e)
30285 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30287 var minScale = this.thumbEl.getWidth() / this.minWidth;
30289 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30290 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30292 this.startScale = this.scale;
30294 while (this.getScaleLevel() < minScale){
30296 this.scale = this.scale + 1;
30298 if(!this.zoomable()){
30303 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30304 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30309 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30316 this.scale = this.startScale;
30318 this.onRotateFail();
30323 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30325 if(this.isDocument){
30326 this.setThumbBoxSize();
30327 this.setThumbBoxPosition();
30328 this.setCanvasPosition();
30333 this.fireEvent('rotate', this, 'right');
30336 onRotateFail : function()
30338 this.errorEl.show(true);
30342 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30347 this.previewEl.dom.innerHTML = '';
30349 var canvasEl = document.createElement("canvas");
30351 var contextEl = canvasEl.getContext("2d");
30353 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30354 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30355 var center = this.imageEl.OriginWidth / 2;
30357 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30358 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30359 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30360 center = this.imageEl.OriginHeight / 2;
30363 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30365 contextEl.translate(center, center);
30366 contextEl.rotate(this.rotate * Math.PI / 180);
30368 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30370 this.canvasEl = document.createElement("canvas");
30372 this.contextEl = this.canvasEl.getContext("2d");
30374 switch (this.rotate) {
30377 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30378 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30380 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30385 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30386 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30388 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30389 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);
30393 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30398 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30399 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30401 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30402 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);
30406 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);
30411 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30412 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30414 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30415 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30419 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);
30426 this.previewEl.appendChild(this.canvasEl);
30428 this.setCanvasPosition();
30433 if(!this.canvasLoaded){
30437 var imageCanvas = document.createElement("canvas");
30439 var imageContext = imageCanvas.getContext("2d");
30441 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30442 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30444 var center = imageCanvas.width / 2;
30446 imageContext.translate(center, center);
30448 imageContext.rotate(this.rotate * Math.PI / 180);
30450 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30452 var canvas = document.createElement("canvas");
30454 var context = canvas.getContext("2d");
30456 canvas.width = this.minWidth;
30457 canvas.height = this.minHeight;
30459 switch (this.rotate) {
30462 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30463 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30465 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30466 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30468 var targetWidth = this.minWidth - 2 * x;
30469 var targetHeight = this.minHeight - 2 * y;
30473 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30474 scale = targetWidth / width;
30477 if(x > 0 && y == 0){
30478 scale = targetHeight / height;
30481 if(x > 0 && y > 0){
30482 scale = targetWidth / width;
30484 if(width < height){
30485 scale = targetHeight / height;
30489 context.scale(scale, scale);
30491 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30492 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30494 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30495 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30497 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30502 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30503 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30505 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30506 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30508 var targetWidth = this.minWidth - 2 * x;
30509 var targetHeight = this.minHeight - 2 * y;
30513 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30514 scale = targetWidth / width;
30517 if(x > 0 && y == 0){
30518 scale = targetHeight / height;
30521 if(x > 0 && y > 0){
30522 scale = targetWidth / width;
30524 if(width < height){
30525 scale = targetHeight / height;
30529 context.scale(scale, scale);
30531 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30532 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30534 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30535 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30537 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30539 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30544 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30545 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30547 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30548 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30550 var targetWidth = this.minWidth - 2 * x;
30551 var targetHeight = this.minHeight - 2 * y;
30555 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30556 scale = targetWidth / width;
30559 if(x > 0 && y == 0){
30560 scale = targetHeight / height;
30563 if(x > 0 && y > 0){
30564 scale = targetWidth / width;
30566 if(width < height){
30567 scale = targetHeight / height;
30571 context.scale(scale, scale);
30573 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30574 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30576 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30577 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30579 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30580 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30582 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30587 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30588 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30590 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30591 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30593 var targetWidth = this.minWidth - 2 * x;
30594 var targetHeight = this.minHeight - 2 * y;
30598 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30599 scale = targetWidth / width;
30602 if(x > 0 && y == 0){
30603 scale = targetHeight / height;
30606 if(x > 0 && y > 0){
30607 scale = targetWidth / width;
30609 if(width < height){
30610 scale = targetHeight / height;
30614 context.scale(scale, scale);
30616 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30617 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30619 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30620 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30622 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30624 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30631 this.cropData = canvas.toDataURL(this.cropType);
30633 if(this.fireEvent('crop', this, this.cropData) !== false){
30634 this.process(this.file, this.cropData);
30641 setThumbBoxSize : function()
30645 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30646 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30647 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30649 this.minWidth = width;
30650 this.minHeight = height;
30652 if(this.rotate == 90 || this.rotate == 270){
30653 this.minWidth = height;
30654 this.minHeight = width;
30659 width = Math.ceil(this.minWidth * height / this.minHeight);
30661 if(this.minWidth > this.minHeight){
30663 height = Math.ceil(this.minHeight * width / this.minWidth);
30666 this.thumbEl.setStyle({
30667 width : width + 'px',
30668 height : height + 'px'
30675 setThumbBoxPosition : function()
30677 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30678 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30680 this.thumbEl.setLeft(x);
30681 this.thumbEl.setTop(y);
30685 baseRotateLevel : function()
30687 this.baseRotate = 1;
30690 typeof(this.exif) != 'undefined' &&
30691 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30692 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30694 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30697 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30701 baseScaleLevel : function()
30705 if(this.isDocument){
30707 if(this.baseRotate == 6 || this.baseRotate == 8){
30709 height = this.thumbEl.getHeight();
30710 this.baseScale = height / this.imageEl.OriginWidth;
30712 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30713 width = this.thumbEl.getWidth();
30714 this.baseScale = width / this.imageEl.OriginHeight;
30720 height = this.thumbEl.getHeight();
30721 this.baseScale = height / this.imageEl.OriginHeight;
30723 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30724 width = this.thumbEl.getWidth();
30725 this.baseScale = width / this.imageEl.OriginWidth;
30731 if(this.baseRotate == 6 || this.baseRotate == 8){
30733 width = this.thumbEl.getHeight();
30734 this.baseScale = width / this.imageEl.OriginHeight;
30736 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30737 height = this.thumbEl.getWidth();
30738 this.baseScale = height / this.imageEl.OriginHeight;
30741 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30742 height = this.thumbEl.getWidth();
30743 this.baseScale = height / this.imageEl.OriginHeight;
30745 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30746 width = this.thumbEl.getHeight();
30747 this.baseScale = width / this.imageEl.OriginWidth;
30754 width = this.thumbEl.getWidth();
30755 this.baseScale = width / this.imageEl.OriginWidth;
30757 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30758 height = this.thumbEl.getHeight();
30759 this.baseScale = height / this.imageEl.OriginHeight;
30762 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30764 height = this.thumbEl.getHeight();
30765 this.baseScale = height / this.imageEl.OriginHeight;
30767 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30768 width = this.thumbEl.getWidth();
30769 this.baseScale = width / this.imageEl.OriginWidth;
30777 getScaleLevel : function()
30779 return this.baseScale * Math.pow(1.1, this.scale);
30782 onTouchStart : function(e)
30784 if(!this.canvasLoaded){
30785 this.beforeSelectFile(e);
30789 var touches = e.browserEvent.touches;
30795 if(touches.length == 1){
30796 this.onMouseDown(e);
30800 if(touches.length != 2){
30806 for(var i = 0, finger; finger = touches[i]; i++){
30807 coords.push(finger.pageX, finger.pageY);
30810 var x = Math.pow(coords[0] - coords[2], 2);
30811 var y = Math.pow(coords[1] - coords[3], 2);
30813 this.startDistance = Math.sqrt(x + y);
30815 this.startScale = this.scale;
30817 this.pinching = true;
30818 this.dragable = false;
30822 onTouchMove : function(e)
30824 if(!this.pinching && !this.dragable){
30828 var touches = e.browserEvent.touches;
30835 this.onMouseMove(e);
30841 for(var i = 0, finger; finger = touches[i]; i++){
30842 coords.push(finger.pageX, finger.pageY);
30845 var x = Math.pow(coords[0] - coords[2], 2);
30846 var y = Math.pow(coords[1] - coords[3], 2);
30848 this.endDistance = Math.sqrt(x + y);
30850 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30852 if(!this.zoomable()){
30853 this.scale = this.startScale;
30861 onTouchEnd : function(e)
30863 this.pinching = false;
30864 this.dragable = false;
30868 process : function(file, crop)
30871 this.maskEl.mask(this.loadingText);
30874 this.xhr = new XMLHttpRequest();
30876 file.xhr = this.xhr;
30878 this.xhr.open(this.method, this.url, true);
30881 "Accept": "application/json",
30882 "Cache-Control": "no-cache",
30883 "X-Requested-With": "XMLHttpRequest"
30886 for (var headerName in headers) {
30887 var headerValue = headers[headerName];
30889 this.xhr.setRequestHeader(headerName, headerValue);
30895 this.xhr.onload = function()
30897 _this.xhrOnLoad(_this.xhr);
30900 this.xhr.onerror = function()
30902 _this.xhrOnError(_this.xhr);
30905 var formData = new FormData();
30907 formData.append('returnHTML', 'NO');
30910 formData.append('crop', crop);
30913 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30914 formData.append(this.paramName, file, file.name);
30917 if(typeof(file.filename) != 'undefined'){
30918 formData.append('filename', file.filename);
30921 if(typeof(file.mimetype) != 'undefined'){
30922 formData.append('mimetype', file.mimetype);
30925 if(this.fireEvent('arrange', this, formData) != false){
30926 this.xhr.send(formData);
30930 xhrOnLoad : function(xhr)
30933 this.maskEl.unmask();
30936 if (xhr.readyState !== 4) {
30937 this.fireEvent('exception', this, xhr);
30941 var response = Roo.decode(xhr.responseText);
30943 if(!response.success){
30944 this.fireEvent('exception', this, xhr);
30948 var response = Roo.decode(xhr.responseText);
30950 this.fireEvent('upload', this, response);
30954 xhrOnError : function()
30957 this.maskEl.unmask();
30960 Roo.log('xhr on error');
30962 var response = Roo.decode(xhr.responseText);
30968 prepare : function(file)
30971 this.maskEl.mask(this.loadingText);
30977 if(typeof(file) === 'string'){
30978 this.loadCanvas(file);
30982 if(!file || !this.urlAPI){
30987 this.cropType = file.type;
30991 if(this.fireEvent('prepare', this, this.file) != false){
30993 var reader = new FileReader();
30995 reader.onload = function (e) {
30996 if (e.target.error) {
30997 Roo.log(e.target.error);
31001 var buffer = e.target.result,
31002 dataView = new DataView(buffer),
31004 maxOffset = dataView.byteLength - 4,
31008 if (dataView.getUint16(0) === 0xffd8) {
31009 while (offset < maxOffset) {
31010 markerBytes = dataView.getUint16(offset);
31012 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31013 markerLength = dataView.getUint16(offset + 2) + 2;
31014 if (offset + markerLength > dataView.byteLength) {
31015 Roo.log('Invalid meta data: Invalid segment size.');
31019 if(markerBytes == 0xffe1){
31020 _this.parseExifData(
31027 offset += markerLength;
31037 var url = _this.urlAPI.createObjectURL(_this.file);
31039 _this.loadCanvas(url);
31044 reader.readAsArrayBuffer(this.file);
31050 parseExifData : function(dataView, offset, length)
31052 var tiffOffset = offset + 10,
31056 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31057 // No Exif data, might be XMP data instead
31061 // Check for the ASCII code for "Exif" (0x45786966):
31062 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31063 // No Exif data, might be XMP data instead
31066 if (tiffOffset + 8 > dataView.byteLength) {
31067 Roo.log('Invalid Exif data: Invalid segment size.');
31070 // Check for the two null bytes:
31071 if (dataView.getUint16(offset + 8) !== 0x0000) {
31072 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31075 // Check the byte alignment:
31076 switch (dataView.getUint16(tiffOffset)) {
31078 littleEndian = true;
31081 littleEndian = false;
31084 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31087 // Check for the TIFF tag marker (0x002A):
31088 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31089 Roo.log('Invalid Exif data: Missing TIFF marker.');
31092 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31093 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31095 this.parseExifTags(
31098 tiffOffset + dirOffset,
31103 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31108 if (dirOffset + 6 > dataView.byteLength) {
31109 Roo.log('Invalid Exif data: Invalid directory offset.');
31112 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31113 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31114 if (dirEndOffset + 4 > dataView.byteLength) {
31115 Roo.log('Invalid Exif data: Invalid directory size.');
31118 for (i = 0; i < tagsNumber; i += 1) {
31122 dirOffset + 2 + 12 * i, // tag offset
31126 // Return the offset to the next directory:
31127 return dataView.getUint32(dirEndOffset, littleEndian);
31130 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31132 var tag = dataView.getUint16(offset, littleEndian);
31134 this.exif[tag] = this.getExifValue(
31138 dataView.getUint16(offset + 2, littleEndian), // tag type
31139 dataView.getUint32(offset + 4, littleEndian), // tag length
31144 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31146 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31155 Roo.log('Invalid Exif data: Invalid tag type.');
31159 tagSize = tagType.size * length;
31160 // Determine if the value is contained in the dataOffset bytes,
31161 // or if the value at the dataOffset is a pointer to the actual data:
31162 dataOffset = tagSize > 4 ?
31163 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31164 if (dataOffset + tagSize > dataView.byteLength) {
31165 Roo.log('Invalid Exif data: Invalid data offset.');
31168 if (length === 1) {
31169 return tagType.getValue(dataView, dataOffset, littleEndian);
31172 for (i = 0; i < length; i += 1) {
31173 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31176 if (tagType.ascii) {
31178 // Concatenate the chars:
31179 for (i = 0; i < values.length; i += 1) {
31181 // Ignore the terminating NULL byte(s):
31182 if (c === '\u0000') {
31194 Roo.apply(Roo.bootstrap.UploadCropbox, {
31196 'Orientation': 0x0112
31200 1: 0, //'top-left',
31202 3: 180, //'bottom-right',
31203 // 4: 'bottom-left',
31205 6: 90, //'right-top',
31206 // 7: 'right-bottom',
31207 8: 270 //'left-bottom'
31211 // byte, 8-bit unsigned int:
31213 getValue: function (dataView, dataOffset) {
31214 return dataView.getUint8(dataOffset);
31218 // ascii, 8-bit byte:
31220 getValue: function (dataView, dataOffset) {
31221 return String.fromCharCode(dataView.getUint8(dataOffset));
31226 // short, 16 bit int:
31228 getValue: function (dataView, dataOffset, littleEndian) {
31229 return dataView.getUint16(dataOffset, littleEndian);
31233 // long, 32 bit int:
31235 getValue: function (dataView, dataOffset, littleEndian) {
31236 return dataView.getUint32(dataOffset, littleEndian);
31240 // rational = two long values, first is numerator, second is denominator:
31242 getValue: function (dataView, dataOffset, littleEndian) {
31243 return dataView.getUint32(dataOffset, littleEndian) /
31244 dataView.getUint32(dataOffset + 4, littleEndian);
31248 // slong, 32 bit signed int:
31250 getValue: function (dataView, dataOffset, littleEndian) {
31251 return dataView.getInt32(dataOffset, littleEndian);
31255 // srational, two slongs, first is numerator, second is denominator:
31257 getValue: function (dataView, dataOffset, littleEndian) {
31258 return dataView.getInt32(dataOffset, littleEndian) /
31259 dataView.getInt32(dataOffset + 4, littleEndian);
31269 cls : 'btn-group roo-upload-cropbox-rotate-left',
31270 action : 'rotate-left',
31274 cls : 'btn btn-default',
31275 html : '<i class="fa fa-undo"></i>'
31281 cls : 'btn-group roo-upload-cropbox-picture',
31282 action : 'picture',
31286 cls : 'btn btn-default',
31287 html : '<i class="fa fa-picture-o"></i>'
31293 cls : 'btn-group roo-upload-cropbox-rotate-right',
31294 action : 'rotate-right',
31298 cls : 'btn btn-default',
31299 html : '<i class="fa fa-repeat"></i>'
31307 cls : 'btn-group roo-upload-cropbox-rotate-left',
31308 action : 'rotate-left',
31312 cls : 'btn btn-default',
31313 html : '<i class="fa fa-undo"></i>'
31319 cls : 'btn-group roo-upload-cropbox-download',
31320 action : 'download',
31324 cls : 'btn btn-default',
31325 html : '<i class="fa fa-download"></i>'
31331 cls : 'btn-group roo-upload-cropbox-crop',
31336 cls : 'btn btn-default',
31337 html : '<i class="fa fa-crop"></i>'
31343 cls : 'btn-group roo-upload-cropbox-trash',
31348 cls : 'btn btn-default',
31349 html : '<i class="fa fa-trash"></i>'
31355 cls : 'btn-group roo-upload-cropbox-rotate-right',
31356 action : 'rotate-right',
31360 cls : 'btn btn-default',
31361 html : '<i class="fa fa-repeat"></i>'
31369 cls : 'btn-group roo-upload-cropbox-rotate-left',
31370 action : 'rotate-left',
31374 cls : 'btn btn-default',
31375 html : '<i class="fa fa-undo"></i>'
31381 cls : 'btn-group roo-upload-cropbox-rotate-right',
31382 action : 'rotate-right',
31386 cls : 'btn btn-default',
31387 html : '<i class="fa fa-repeat"></i>'
31400 * @class Roo.bootstrap.DocumentManager
31401 * @extends Roo.bootstrap.Component
31402 * Bootstrap DocumentManager class
31403 * @cfg {String} paramName default 'imageUpload'
31404 * @cfg {String} toolTipName default 'filename'
31405 * @cfg {String} method default POST
31406 * @cfg {String} url action url
31407 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31408 * @cfg {Boolean} multiple multiple upload default true
31409 * @cfg {Number} thumbSize default 300
31410 * @cfg {String} fieldLabel
31411 * @cfg {Number} labelWidth default 4
31412 * @cfg {String} labelAlign (left|top) default left
31413 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31414 * @cfg {Number} labellg set the width of label (1-12)
31415 * @cfg {Number} labelmd set the width of label (1-12)
31416 * @cfg {Number} labelsm set the width of label (1-12)
31417 * @cfg {Number} labelxs set the width of label (1-12)
31420 * Create a new DocumentManager
31421 * @param {Object} config The config object
31424 Roo.bootstrap.DocumentManager = function(config){
31425 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31428 this.delegates = [];
31433 * Fire when initial the DocumentManager
31434 * @param {Roo.bootstrap.DocumentManager} this
31439 * inspect selected file
31440 * @param {Roo.bootstrap.DocumentManager} this
31441 * @param {File} file
31446 * Fire when xhr load exception
31447 * @param {Roo.bootstrap.DocumentManager} this
31448 * @param {XMLHttpRequest} xhr
31450 "exception" : true,
31452 * @event afterupload
31453 * Fire when xhr load exception
31454 * @param {Roo.bootstrap.DocumentManager} this
31455 * @param {XMLHttpRequest} xhr
31457 "afterupload" : true,
31460 * prepare the form data
31461 * @param {Roo.bootstrap.DocumentManager} this
31462 * @param {Object} formData
31467 * Fire when remove the file
31468 * @param {Roo.bootstrap.DocumentManager} this
31469 * @param {Object} file
31474 * Fire after refresh the file
31475 * @param {Roo.bootstrap.DocumentManager} this
31480 * Fire after click the image
31481 * @param {Roo.bootstrap.DocumentManager} this
31482 * @param {Object} file
31487 * Fire when upload a image and editable set to true
31488 * @param {Roo.bootstrap.DocumentManager} this
31489 * @param {Object} file
31493 * @event beforeselectfile
31494 * Fire before select file
31495 * @param {Roo.bootstrap.DocumentManager} this
31497 "beforeselectfile" : true,
31500 * Fire before process file
31501 * @param {Roo.bootstrap.DocumentManager} this
31502 * @param {Object} file
31506 * @event previewrendered
31507 * Fire when preview rendered
31508 * @param {Roo.bootstrap.DocumentManager} this
31509 * @param {Object} file
31511 "previewrendered" : true,
31514 "previewResize" : true
31519 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31528 paramName : 'imageUpload',
31529 toolTipName : 'filename',
31532 labelAlign : 'left',
31542 getAutoCreate : function()
31544 var managerWidget = {
31546 cls : 'roo-document-manager',
31550 cls : 'roo-document-manager-selector',
31555 cls : 'roo-document-manager-uploader',
31559 cls : 'roo-document-manager-upload-btn',
31560 html : '<i class="fa fa-plus"></i>'
31571 cls : 'column col-md-12',
31576 if(this.fieldLabel.length){
31581 cls : 'column col-md-12',
31582 html : this.fieldLabel
31586 cls : 'column col-md-12',
31591 if(this.labelAlign == 'left'){
31596 html : this.fieldLabel
31605 if(this.labelWidth > 12){
31606 content[0].style = "width: " + this.labelWidth + 'px';
31609 if(this.labelWidth < 13 && this.labelmd == 0){
31610 this.labelmd = this.labelWidth;
31613 if(this.labellg > 0){
31614 content[0].cls += ' col-lg-' + this.labellg;
31615 content[1].cls += ' col-lg-' + (12 - this.labellg);
31618 if(this.labelmd > 0){
31619 content[0].cls += ' col-md-' + this.labelmd;
31620 content[1].cls += ' col-md-' + (12 - this.labelmd);
31623 if(this.labelsm > 0){
31624 content[0].cls += ' col-sm-' + this.labelsm;
31625 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31628 if(this.labelxs > 0){
31629 content[0].cls += ' col-xs-' + this.labelxs;
31630 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31638 cls : 'row clearfix',
31646 initEvents : function()
31648 this.managerEl = this.el.select('.roo-document-manager', true).first();
31649 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31651 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31652 this.selectorEl.hide();
31655 this.selectorEl.attr('multiple', 'multiple');
31658 this.selectorEl.on('change', this.onFileSelected, this);
31660 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31661 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31663 this.uploader.on('click', this.onUploaderClick, this);
31665 this.renderProgressDialog();
31669 window.addEventListener("resize", function() { _this.refresh(); } );
31671 this.fireEvent('initial', this);
31674 renderProgressDialog : function()
31678 this.progressDialog = new Roo.bootstrap.Modal({
31679 cls : 'roo-document-manager-progress-dialog',
31680 allow_close : false,
31691 btnclick : function() {
31692 _this.uploadCancel();
31698 this.progressDialog.render(Roo.get(document.body));
31700 this.progress = new Roo.bootstrap.Progress({
31701 cls : 'roo-document-manager-progress',
31706 this.progress.render(this.progressDialog.getChildContainer());
31708 this.progressBar = new Roo.bootstrap.ProgressBar({
31709 cls : 'roo-document-manager-progress-bar',
31712 aria_valuemax : 12,
31716 this.progressBar.render(this.progress.getChildContainer());
31719 onUploaderClick : function(e)
31721 e.preventDefault();
31723 if(this.fireEvent('beforeselectfile', this) != false){
31724 this.selectorEl.dom.click();
31729 onFileSelected : function(e)
31731 e.preventDefault();
31733 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31737 Roo.each(this.selectorEl.dom.files, function(file){
31738 if(this.fireEvent('inspect', this, file) != false){
31739 this.files.push(file);
31749 this.selectorEl.dom.value = '';
31751 if(!this.files || !this.files.length){
31755 if(this.boxes > 0 && this.files.length > this.boxes){
31756 this.files = this.files.slice(0, this.boxes);
31759 this.uploader.show();
31761 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31762 this.uploader.hide();
31771 Roo.each(this.files, function(file){
31773 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31774 var f = this.renderPreview(file);
31779 if(file.type.indexOf('image') != -1){
31780 this.delegates.push(
31782 _this.process(file);
31783 }).createDelegate(this)
31791 _this.process(file);
31792 }).createDelegate(this)
31797 this.files = files;
31799 this.delegates = this.delegates.concat(docs);
31801 if(!this.delegates.length){
31806 this.progressBar.aria_valuemax = this.delegates.length;
31813 arrange : function()
31815 if(!this.delegates.length){
31816 this.progressDialog.hide();
31821 var delegate = this.delegates.shift();
31823 this.progressDialog.show();
31825 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31827 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31832 refresh : function()
31834 this.uploader.show();
31836 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31837 this.uploader.hide();
31840 Roo.isTouch ? this.closable(false) : this.closable(true);
31842 this.fireEvent('refresh', this);
31845 onRemove : function(e, el, o)
31847 e.preventDefault();
31849 this.fireEvent('remove', this, o);
31853 remove : function(o)
31857 Roo.each(this.files, function(file){
31858 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31867 this.files = files;
31874 Roo.each(this.files, function(file){
31879 file.target.remove();
31888 onClick : function(e, el, o)
31890 e.preventDefault();
31892 this.fireEvent('click', this, o);
31896 closable : function(closable)
31898 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31900 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31912 xhrOnLoad : function(xhr)
31914 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31918 if (xhr.readyState !== 4) {
31920 this.fireEvent('exception', this, xhr);
31924 var response = Roo.decode(xhr.responseText);
31926 if(!response.success){
31928 this.fireEvent('exception', this, xhr);
31932 var file = this.renderPreview(response.data);
31934 this.files.push(file);
31938 this.fireEvent('afterupload', this, xhr);
31942 xhrOnError : function(xhr)
31944 Roo.log('xhr on error');
31946 var response = Roo.decode(xhr.responseText);
31953 process : function(file)
31955 if(this.fireEvent('process', this, file) !== false){
31956 if(this.editable && file.type.indexOf('image') != -1){
31957 this.fireEvent('edit', this, file);
31961 this.uploadStart(file, false);
31968 uploadStart : function(file, crop)
31970 this.xhr = new XMLHttpRequest();
31972 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31977 file.xhr = this.xhr;
31979 this.managerEl.createChild({
31981 cls : 'roo-document-manager-loading',
31985 tooltip : file.name,
31986 cls : 'roo-document-manager-thumb',
31987 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31993 this.xhr.open(this.method, this.url, true);
31996 "Accept": "application/json",
31997 "Cache-Control": "no-cache",
31998 "X-Requested-With": "XMLHttpRequest"
32001 for (var headerName in headers) {
32002 var headerValue = headers[headerName];
32004 this.xhr.setRequestHeader(headerName, headerValue);
32010 this.xhr.onload = function()
32012 _this.xhrOnLoad(_this.xhr);
32015 this.xhr.onerror = function()
32017 _this.xhrOnError(_this.xhr);
32020 var formData = new FormData();
32022 formData.append('returnHTML', 'NO');
32025 formData.append('crop', crop);
32028 formData.append(this.paramName, file, file.name);
32035 if(this.fireEvent('prepare', this, formData, options) != false){
32037 if(options.manually){
32041 this.xhr.send(formData);
32045 this.uploadCancel();
32048 uploadCancel : function()
32054 this.delegates = [];
32056 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32063 renderPreview : function(file)
32065 if(typeof(file.target) != 'undefined' && file.target){
32069 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32071 var previewEl = this.managerEl.createChild({
32073 cls : 'roo-document-manager-preview',
32077 tooltip : file[this.toolTipName],
32078 cls : 'roo-document-manager-thumb',
32079 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32084 html : '<i class="fa fa-times-circle"></i>'
32089 var close = previewEl.select('button.close', true).first();
32091 close.on('click', this.onRemove, this, file);
32093 file.target = previewEl;
32095 var image = previewEl.select('img', true).first();
32099 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32101 image.on('click', this.onClick, this, file);
32103 this.fireEvent('previewrendered', this, file);
32109 onPreviewLoad : function(file, image)
32111 if(typeof(file.target) == 'undefined' || !file.target){
32115 var width = image.dom.naturalWidth || image.dom.width;
32116 var height = image.dom.naturalHeight || image.dom.height;
32118 if(!this.previewResize) {
32122 if(width > height){
32123 file.target.addClass('wide');
32127 file.target.addClass('tall');
32132 uploadFromSource : function(file, crop)
32134 this.xhr = new XMLHttpRequest();
32136 this.managerEl.createChild({
32138 cls : 'roo-document-manager-loading',
32142 tooltip : file.name,
32143 cls : 'roo-document-manager-thumb',
32144 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32150 this.xhr.open(this.method, this.url, true);
32153 "Accept": "application/json",
32154 "Cache-Control": "no-cache",
32155 "X-Requested-With": "XMLHttpRequest"
32158 for (var headerName in headers) {
32159 var headerValue = headers[headerName];
32161 this.xhr.setRequestHeader(headerName, headerValue);
32167 this.xhr.onload = function()
32169 _this.xhrOnLoad(_this.xhr);
32172 this.xhr.onerror = function()
32174 _this.xhrOnError(_this.xhr);
32177 var formData = new FormData();
32179 formData.append('returnHTML', 'NO');
32181 formData.append('crop', crop);
32183 if(typeof(file.filename) != 'undefined'){
32184 formData.append('filename', file.filename);
32187 if(typeof(file.mimetype) != 'undefined'){
32188 formData.append('mimetype', file.mimetype);
32193 if(this.fireEvent('prepare', this, formData) != false){
32194 this.xhr.send(formData);
32204 * @class Roo.bootstrap.DocumentViewer
32205 * @extends Roo.bootstrap.Component
32206 * Bootstrap DocumentViewer class
32207 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32208 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32211 * Create a new DocumentViewer
32212 * @param {Object} config The config object
32215 Roo.bootstrap.DocumentViewer = function(config){
32216 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32221 * Fire after initEvent
32222 * @param {Roo.bootstrap.DocumentViewer} this
32228 * @param {Roo.bootstrap.DocumentViewer} this
32233 * Fire after download button
32234 * @param {Roo.bootstrap.DocumentViewer} this
32239 * Fire after trash button
32240 * @param {Roo.bootstrap.DocumentViewer} this
32247 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32249 showDownload : true,
32253 getAutoCreate : function()
32257 cls : 'roo-document-viewer',
32261 cls : 'roo-document-viewer-body',
32265 cls : 'roo-document-viewer-thumb',
32269 cls : 'roo-document-viewer-image'
32277 cls : 'roo-document-viewer-footer',
32280 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32284 cls : 'btn-group roo-document-viewer-download',
32288 cls : 'btn btn-default',
32289 html : '<i class="fa fa-download"></i>'
32295 cls : 'btn-group roo-document-viewer-trash',
32299 cls : 'btn btn-default',
32300 html : '<i class="fa fa-trash"></i>'
32313 initEvents : function()
32315 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32316 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32318 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32319 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32321 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32322 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32324 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32325 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32327 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32328 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32330 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32331 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32333 this.bodyEl.on('click', this.onClick, this);
32334 this.downloadBtn.on('click', this.onDownload, this);
32335 this.trashBtn.on('click', this.onTrash, this);
32337 this.downloadBtn.hide();
32338 this.trashBtn.hide();
32340 if(this.showDownload){
32341 this.downloadBtn.show();
32344 if(this.showTrash){
32345 this.trashBtn.show();
32348 if(!this.showDownload && !this.showTrash) {
32349 this.footerEl.hide();
32354 initial : function()
32356 this.fireEvent('initial', this);
32360 onClick : function(e)
32362 e.preventDefault();
32364 this.fireEvent('click', this);
32367 onDownload : function(e)
32369 e.preventDefault();
32371 this.fireEvent('download', this);
32374 onTrash : function(e)
32376 e.preventDefault();
32378 this.fireEvent('trash', this);
32390 * @class Roo.bootstrap.NavProgressBar
32391 * @extends Roo.bootstrap.Component
32392 * Bootstrap NavProgressBar class
32395 * Create a new nav progress bar
32396 * @param {Object} config The config object
32399 Roo.bootstrap.NavProgressBar = function(config){
32400 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32402 this.bullets = this.bullets || [];
32404 // Roo.bootstrap.NavProgressBar.register(this);
32408 * Fires when the active item changes
32409 * @param {Roo.bootstrap.NavProgressBar} this
32410 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32411 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32418 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32423 getAutoCreate : function()
32425 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32429 cls : 'roo-navigation-bar-group',
32433 cls : 'roo-navigation-top-bar'
32437 cls : 'roo-navigation-bullets-bar',
32441 cls : 'roo-navigation-bar'
32448 cls : 'roo-navigation-bottom-bar'
32458 initEvents: function()
32463 onRender : function(ct, position)
32465 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32467 if(this.bullets.length){
32468 Roo.each(this.bullets, function(b){
32477 addItem : function(cfg)
32479 var item = new Roo.bootstrap.NavProgressItem(cfg);
32481 item.parentId = this.id;
32482 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32485 var top = new Roo.bootstrap.Element({
32487 cls : 'roo-navigation-bar-text'
32490 var bottom = new Roo.bootstrap.Element({
32492 cls : 'roo-navigation-bar-text'
32495 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32496 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32498 var topText = new Roo.bootstrap.Element({
32500 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32503 var bottomText = new Roo.bootstrap.Element({
32505 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32508 topText.onRender(top.el, null);
32509 bottomText.onRender(bottom.el, null);
32512 item.bottomEl = bottom;
32515 this.barItems.push(item);
32520 getActive : function()
32522 var active = false;
32524 Roo.each(this.barItems, function(v){
32526 if (!v.isActive()) {
32538 setActiveItem : function(item)
32542 Roo.each(this.barItems, function(v){
32543 if (v.rid == item.rid) {
32547 if (v.isActive()) {
32548 v.setActive(false);
32553 item.setActive(true);
32555 this.fireEvent('changed', this, item, prev);
32558 getBarItem: function(rid)
32562 Roo.each(this.barItems, function(e) {
32563 if (e.rid != rid) {
32574 indexOfItem : function(item)
32578 Roo.each(this.barItems, function(v, i){
32580 if (v.rid != item.rid) {
32591 setActiveNext : function()
32593 var i = this.indexOfItem(this.getActive());
32595 if (i > this.barItems.length) {
32599 this.setActiveItem(this.barItems[i+1]);
32602 setActivePrev : function()
32604 var i = this.indexOfItem(this.getActive());
32610 this.setActiveItem(this.barItems[i-1]);
32613 format : function()
32615 if(!this.barItems.length){
32619 var width = 100 / this.barItems.length;
32621 Roo.each(this.barItems, function(i){
32622 i.el.setStyle('width', width + '%');
32623 i.topEl.el.setStyle('width', width + '%');
32624 i.bottomEl.el.setStyle('width', width + '%');
32633 * Nav Progress Item
32638 * @class Roo.bootstrap.NavProgressItem
32639 * @extends Roo.bootstrap.Component
32640 * Bootstrap NavProgressItem class
32641 * @cfg {String} rid the reference id
32642 * @cfg {Boolean} active (true|false) Is item active default false
32643 * @cfg {Boolean} disabled (true|false) Is item active default false
32644 * @cfg {String} html
32645 * @cfg {String} position (top|bottom) text position default bottom
32646 * @cfg {String} icon show icon instead of number
32649 * Create a new NavProgressItem
32650 * @param {Object} config The config object
32652 Roo.bootstrap.NavProgressItem = function(config){
32653 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32658 * The raw click event for the entire grid.
32659 * @param {Roo.bootstrap.NavProgressItem} this
32660 * @param {Roo.EventObject} e
32667 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32673 position : 'bottom',
32676 getAutoCreate : function()
32678 var iconCls = 'roo-navigation-bar-item-icon';
32680 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32684 cls: 'roo-navigation-bar-item',
32694 cfg.cls += ' active';
32697 cfg.cls += ' disabled';
32703 disable : function()
32705 this.setDisabled(true);
32708 enable : function()
32710 this.setDisabled(false);
32713 initEvents: function()
32715 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32717 this.iconEl.on('click', this.onClick, this);
32720 onClick : function(e)
32722 e.preventDefault();
32728 if(this.fireEvent('click', this, e) === false){
32732 this.parent().setActiveItem(this);
32735 isActive: function ()
32737 return this.active;
32740 setActive : function(state)
32742 if(this.active == state){
32746 this.active = state;
32749 this.el.addClass('active');
32753 this.el.removeClass('active');
32758 setDisabled : function(state)
32760 if(this.disabled == state){
32764 this.disabled = state;
32767 this.el.addClass('disabled');
32771 this.el.removeClass('disabled');
32774 tooltipEl : function()
32776 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32789 * @class Roo.bootstrap.FieldLabel
32790 * @extends Roo.bootstrap.Component
32791 * Bootstrap FieldLabel class
32792 * @cfg {String} html contents of the element
32793 * @cfg {String} tag tag of the element default label
32794 * @cfg {String} cls class of the element
32795 * @cfg {String} target label target
32796 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32797 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32798 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32799 * @cfg {String} iconTooltip default "This field is required"
32800 * @cfg {String} indicatorpos (left|right) default left
32803 * Create a new FieldLabel
32804 * @param {Object} config The config object
32807 Roo.bootstrap.FieldLabel = function(config){
32808 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32813 * Fires after the field has been marked as invalid.
32814 * @param {Roo.form.FieldLabel} this
32815 * @param {String} msg The validation message
32820 * Fires after the field has been validated with no errors.
32821 * @param {Roo.form.FieldLabel} this
32827 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32834 invalidClass : 'has-warning',
32835 validClass : 'has-success',
32836 iconTooltip : 'This field is required',
32837 indicatorpos : 'left',
32839 getAutoCreate : function(){
32842 if (!this.allowBlank) {
32848 cls : 'roo-bootstrap-field-label ' + this.cls,
32853 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32854 tooltip : this.iconTooltip
32863 if(this.indicatorpos == 'right'){
32866 cls : 'roo-bootstrap-field-label ' + this.cls,
32875 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32876 tooltip : this.iconTooltip
32885 initEvents: function()
32887 Roo.bootstrap.Element.superclass.initEvents.call(this);
32889 this.indicator = this.indicatorEl();
32891 if(this.indicator){
32892 this.indicator.removeClass('visible');
32893 this.indicator.addClass('invisible');
32896 Roo.bootstrap.FieldLabel.register(this);
32899 indicatorEl : function()
32901 var indicator = this.el.select('i.roo-required-indicator',true).first();
32912 * Mark this field as valid
32914 markValid : function()
32916 if(this.indicator){
32917 this.indicator.removeClass('visible');
32918 this.indicator.addClass('invisible');
32920 if (Roo.bootstrap.version == 3) {
32921 this.el.removeClass(this.invalidClass);
32922 this.el.addClass(this.validClass);
32924 this.el.removeClass('is-invalid');
32925 this.el.addClass('is-valid');
32929 this.fireEvent('valid', this);
32933 * Mark this field as invalid
32934 * @param {String} msg The validation message
32936 markInvalid : function(msg)
32938 if(this.indicator){
32939 this.indicator.removeClass('invisible');
32940 this.indicator.addClass('visible');
32942 if (Roo.bootstrap.version == 3) {
32943 this.el.removeClass(this.validClass);
32944 this.el.addClass(this.invalidClass);
32946 this.el.removeClass('is-valid');
32947 this.el.addClass('is-invalid');
32951 this.fireEvent('invalid', this, msg);
32957 Roo.apply(Roo.bootstrap.FieldLabel, {
32962 * register a FieldLabel Group
32963 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32965 register : function(label)
32967 if(this.groups.hasOwnProperty(label.target)){
32971 this.groups[label.target] = label;
32975 * fetch a FieldLabel Group based on the target
32976 * @param {string} target
32977 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32979 get: function(target) {
32980 if (typeof(this.groups[target]) == 'undefined') {
32984 return this.groups[target] ;
32993 * page DateSplitField.
32999 * @class Roo.bootstrap.DateSplitField
33000 * @extends Roo.bootstrap.Component
33001 * Bootstrap DateSplitField class
33002 * @cfg {string} fieldLabel - the label associated
33003 * @cfg {Number} labelWidth set the width of label (0-12)
33004 * @cfg {String} labelAlign (top|left)
33005 * @cfg {Boolean} dayAllowBlank (true|false) default false
33006 * @cfg {Boolean} monthAllowBlank (true|false) default false
33007 * @cfg {Boolean} yearAllowBlank (true|false) default false
33008 * @cfg {string} dayPlaceholder
33009 * @cfg {string} monthPlaceholder
33010 * @cfg {string} yearPlaceholder
33011 * @cfg {string} dayFormat default 'd'
33012 * @cfg {string} monthFormat default 'm'
33013 * @cfg {string} yearFormat default 'Y'
33014 * @cfg {Number} labellg set the width of label (1-12)
33015 * @cfg {Number} labelmd set the width of label (1-12)
33016 * @cfg {Number} labelsm set the width of label (1-12)
33017 * @cfg {Number} labelxs set the width of label (1-12)
33021 * Create a new DateSplitField
33022 * @param {Object} config The config object
33025 Roo.bootstrap.DateSplitField = function(config){
33026 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33032 * getting the data of years
33033 * @param {Roo.bootstrap.DateSplitField} this
33034 * @param {Object} years
33039 * getting the data of days
33040 * @param {Roo.bootstrap.DateSplitField} this
33041 * @param {Object} days
33046 * Fires after the field has been marked as invalid.
33047 * @param {Roo.form.Field} this
33048 * @param {String} msg The validation message
33053 * Fires after the field has been validated with no errors.
33054 * @param {Roo.form.Field} this
33060 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33063 labelAlign : 'top',
33065 dayAllowBlank : false,
33066 monthAllowBlank : false,
33067 yearAllowBlank : false,
33068 dayPlaceholder : '',
33069 monthPlaceholder : '',
33070 yearPlaceholder : '',
33074 isFormField : true,
33080 getAutoCreate : function()
33084 cls : 'row roo-date-split-field-group',
33089 cls : 'form-hidden-field roo-date-split-field-group-value',
33095 var labelCls = 'col-md-12';
33096 var contentCls = 'col-md-4';
33098 if(this.fieldLabel){
33102 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33106 html : this.fieldLabel
33111 if(this.labelAlign == 'left'){
33113 if(this.labelWidth > 12){
33114 label.style = "width: " + this.labelWidth + 'px';
33117 if(this.labelWidth < 13 && this.labelmd == 0){
33118 this.labelmd = this.labelWidth;
33121 if(this.labellg > 0){
33122 labelCls = ' col-lg-' + this.labellg;
33123 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33126 if(this.labelmd > 0){
33127 labelCls = ' col-md-' + this.labelmd;
33128 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33131 if(this.labelsm > 0){
33132 labelCls = ' col-sm-' + this.labelsm;
33133 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33136 if(this.labelxs > 0){
33137 labelCls = ' col-xs-' + this.labelxs;
33138 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33142 label.cls += ' ' + labelCls;
33144 cfg.cn.push(label);
33147 Roo.each(['day', 'month', 'year'], function(t){
33150 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33157 inputEl: function ()
33159 return this.el.select('.roo-date-split-field-group-value', true).first();
33162 onRender : function(ct, position)
33166 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33168 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33170 this.dayField = new Roo.bootstrap.ComboBox({
33171 allowBlank : this.dayAllowBlank,
33172 alwaysQuery : true,
33173 displayField : 'value',
33176 forceSelection : true,
33178 placeholder : this.dayPlaceholder,
33179 selectOnFocus : true,
33180 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33181 triggerAction : 'all',
33183 valueField : 'value',
33184 store : new Roo.data.SimpleStore({
33185 data : (function() {
33187 _this.fireEvent('days', _this, days);
33190 fields : [ 'value' ]
33193 select : function (_self, record, index)
33195 _this.setValue(_this.getValue());
33200 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33202 this.monthField = new Roo.bootstrap.MonthField({
33203 after : '<i class=\"fa fa-calendar\"></i>',
33204 allowBlank : this.monthAllowBlank,
33205 placeholder : this.monthPlaceholder,
33208 render : function (_self)
33210 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33211 e.preventDefault();
33215 select : function (_self, oldvalue, newvalue)
33217 _this.setValue(_this.getValue());
33222 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33224 this.yearField = new Roo.bootstrap.ComboBox({
33225 allowBlank : this.yearAllowBlank,
33226 alwaysQuery : true,
33227 displayField : 'value',
33230 forceSelection : true,
33232 placeholder : this.yearPlaceholder,
33233 selectOnFocus : true,
33234 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33235 triggerAction : 'all',
33237 valueField : 'value',
33238 store : new Roo.data.SimpleStore({
33239 data : (function() {
33241 _this.fireEvent('years', _this, years);
33244 fields : [ 'value' ]
33247 select : function (_self, record, index)
33249 _this.setValue(_this.getValue());
33254 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33257 setValue : function(v, format)
33259 this.inputEl.dom.value = v;
33261 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33263 var d = Date.parseDate(v, f);
33270 this.setDay(d.format(this.dayFormat));
33271 this.setMonth(d.format(this.monthFormat));
33272 this.setYear(d.format(this.yearFormat));
33279 setDay : function(v)
33281 this.dayField.setValue(v);
33282 this.inputEl.dom.value = this.getValue();
33287 setMonth : function(v)
33289 this.monthField.setValue(v, true);
33290 this.inputEl.dom.value = this.getValue();
33295 setYear : function(v)
33297 this.yearField.setValue(v);
33298 this.inputEl.dom.value = this.getValue();
33303 getDay : function()
33305 return this.dayField.getValue();
33308 getMonth : function()
33310 return this.monthField.getValue();
33313 getYear : function()
33315 return this.yearField.getValue();
33318 getValue : function()
33320 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33322 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33332 this.inputEl.dom.value = '';
33337 validate : function()
33339 var d = this.dayField.validate();
33340 var m = this.monthField.validate();
33341 var y = this.yearField.validate();
33346 (!this.dayAllowBlank && !d) ||
33347 (!this.monthAllowBlank && !m) ||
33348 (!this.yearAllowBlank && !y)
33353 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33362 this.markInvalid();
33367 markValid : function()
33370 var label = this.el.select('label', true).first();
33371 var icon = this.el.select('i.fa-star', true).first();
33377 this.fireEvent('valid', this);
33381 * Mark this field as invalid
33382 * @param {String} msg The validation message
33384 markInvalid : function(msg)
33387 var label = this.el.select('label', true).first();
33388 var icon = this.el.select('i.fa-star', true).first();
33390 if(label && !icon){
33391 this.el.select('.roo-date-split-field-label', true).createChild({
33393 cls : 'text-danger fa fa-lg fa-star',
33394 tooltip : 'This field is required',
33395 style : 'margin-right:5px;'
33399 this.fireEvent('invalid', this, msg);
33402 clearInvalid : function()
33404 var label = this.el.select('label', true).first();
33405 var icon = this.el.select('i.fa-star', true).first();
33411 this.fireEvent('valid', this);
33414 getName: function()
33424 * http://masonry.desandro.com
33426 * The idea is to render all the bricks based on vertical width...
33428 * The original code extends 'outlayer' - we might need to use that....
33434 * @class Roo.bootstrap.LayoutMasonry
33435 * @extends Roo.bootstrap.Component
33436 * Bootstrap Layout Masonry class
33439 * Create a new Element
33440 * @param {Object} config The config object
33443 Roo.bootstrap.LayoutMasonry = function(config){
33445 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33449 Roo.bootstrap.LayoutMasonry.register(this);
33455 * Fire after layout the items
33456 * @param {Roo.bootstrap.LayoutMasonry} this
33457 * @param {Roo.EventObject} e
33464 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33467 * @cfg {Boolean} isLayoutInstant = no animation?
33469 isLayoutInstant : false, // needed?
33472 * @cfg {Number} boxWidth width of the columns
33477 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33482 * @cfg {Number} padWidth padding below box..
33487 * @cfg {Number} gutter gutter width..
33492 * @cfg {Number} maxCols maximum number of columns
33498 * @cfg {Boolean} isAutoInitial defalut true
33500 isAutoInitial : true,
33505 * @cfg {Boolean} isHorizontal defalut false
33507 isHorizontal : false,
33509 currentSize : null,
33515 bricks: null, //CompositeElement
33519 _isLayoutInited : false,
33521 // isAlternative : false, // only use for vertical layout...
33524 * @cfg {Number} alternativePadWidth padding below box..
33526 alternativePadWidth : 50,
33528 selectedBrick : [],
33530 getAutoCreate : function(){
33532 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33536 cls: 'blog-masonary-wrapper ' + this.cls,
33538 cls : 'mas-boxes masonary'
33545 getChildContainer: function( )
33547 if (this.boxesEl) {
33548 return this.boxesEl;
33551 this.boxesEl = this.el.select('.mas-boxes').first();
33553 return this.boxesEl;
33557 initEvents : function()
33561 if(this.isAutoInitial){
33562 Roo.log('hook children rendered');
33563 this.on('childrenrendered', function() {
33564 Roo.log('children rendered');
33570 initial : function()
33572 this.selectedBrick = [];
33574 this.currentSize = this.el.getBox(true);
33576 Roo.EventManager.onWindowResize(this.resize, this);
33578 if(!this.isAutoInitial){
33586 //this.layout.defer(500,this);
33590 resize : function()
33592 var cs = this.el.getBox(true);
33595 this.currentSize.width == cs.width &&
33596 this.currentSize.x == cs.x &&
33597 this.currentSize.height == cs.height &&
33598 this.currentSize.y == cs.y
33600 Roo.log("no change in with or X or Y");
33604 this.currentSize = cs;
33610 layout : function()
33612 this._resetLayout();
33614 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33616 this.layoutItems( isInstant );
33618 this._isLayoutInited = true;
33620 this.fireEvent('layout', this);
33624 _resetLayout : function()
33626 if(this.isHorizontal){
33627 this.horizontalMeasureColumns();
33631 this.verticalMeasureColumns();
33635 verticalMeasureColumns : function()
33637 this.getContainerWidth();
33639 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33640 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33644 var boxWidth = this.boxWidth + this.padWidth;
33646 if(this.containerWidth < this.boxWidth){
33647 boxWidth = this.containerWidth
33650 var containerWidth = this.containerWidth;
33652 var cols = Math.floor(containerWidth / boxWidth);
33654 this.cols = Math.max( cols, 1 );
33656 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33658 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33660 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33662 this.colWidth = boxWidth + avail - this.padWidth;
33664 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33665 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33668 horizontalMeasureColumns : function()
33670 this.getContainerWidth();
33672 var boxWidth = this.boxWidth;
33674 if(this.containerWidth < boxWidth){
33675 boxWidth = this.containerWidth;
33678 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33680 this.el.setHeight(boxWidth);
33684 getContainerWidth : function()
33686 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33689 layoutItems : function( isInstant )
33691 Roo.log(this.bricks);
33693 var items = Roo.apply([], this.bricks);
33695 if(this.isHorizontal){
33696 this._horizontalLayoutItems( items , isInstant );
33700 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33701 // this._verticalAlternativeLayoutItems( items , isInstant );
33705 this._verticalLayoutItems( items , isInstant );
33709 _verticalLayoutItems : function ( items , isInstant)
33711 if ( !items || !items.length ) {
33716 ['xs', 'xs', 'xs', 'tall'],
33717 ['xs', 'xs', 'tall'],
33718 ['xs', 'xs', 'sm'],
33719 ['xs', 'xs', 'xs'],
33725 ['sm', 'xs', 'xs'],
33729 ['tall', 'xs', 'xs', 'xs'],
33730 ['tall', 'xs', 'xs'],
33742 Roo.each(items, function(item, k){
33744 switch (item.size) {
33745 // these layouts take up a full box,
33756 boxes.push([item]);
33779 var filterPattern = function(box, length)
33787 var pattern = box.slice(0, length);
33791 Roo.each(pattern, function(i){
33792 format.push(i.size);
33795 Roo.each(standard, function(s){
33797 if(String(s) != String(format)){
33806 if(!match && length == 1){
33811 filterPattern(box, length - 1);
33815 queue.push(pattern);
33817 box = box.slice(length, box.length);
33819 filterPattern(box, 4);
33825 Roo.each(boxes, function(box, k){
33831 if(box.length == 1){
33836 filterPattern(box, 4);
33840 this._processVerticalLayoutQueue( queue, isInstant );
33844 // _verticalAlternativeLayoutItems : function( items , isInstant )
33846 // if ( !items || !items.length ) {
33850 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33854 _horizontalLayoutItems : function ( items , isInstant)
33856 if ( !items || !items.length || items.length < 3) {
33862 var eItems = items.slice(0, 3);
33864 items = items.slice(3, items.length);
33867 ['xs', 'xs', 'xs', 'wide'],
33868 ['xs', 'xs', 'wide'],
33869 ['xs', 'xs', 'sm'],
33870 ['xs', 'xs', 'xs'],
33876 ['sm', 'xs', 'xs'],
33880 ['wide', 'xs', 'xs', 'xs'],
33881 ['wide', 'xs', 'xs'],
33894 Roo.each(items, function(item, k){
33896 switch (item.size) {
33907 boxes.push([item]);
33931 var filterPattern = function(box, length)
33939 var pattern = box.slice(0, length);
33943 Roo.each(pattern, function(i){
33944 format.push(i.size);
33947 Roo.each(standard, function(s){
33949 if(String(s) != String(format)){
33958 if(!match && length == 1){
33963 filterPattern(box, length - 1);
33967 queue.push(pattern);
33969 box = box.slice(length, box.length);
33971 filterPattern(box, 4);
33977 Roo.each(boxes, function(box, k){
33983 if(box.length == 1){
33988 filterPattern(box, 4);
33995 var pos = this.el.getBox(true);
33999 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34001 var hit_end = false;
34003 Roo.each(queue, function(box){
34007 Roo.each(box, function(b){
34009 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34019 Roo.each(box, function(b){
34021 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34024 mx = Math.max(mx, b.x);
34028 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34032 Roo.each(box, function(b){
34034 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34048 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34051 /** Sets position of item in DOM
34052 * @param {Element} item
34053 * @param {Number} x - horizontal position
34054 * @param {Number} y - vertical position
34055 * @param {Boolean} isInstant - disables transitions
34057 _processVerticalLayoutQueue : function( queue, isInstant )
34059 var pos = this.el.getBox(true);
34064 for (var i = 0; i < this.cols; i++){
34068 Roo.each(queue, function(box, k){
34070 var col = k % this.cols;
34072 Roo.each(box, function(b,kk){
34074 b.el.position('absolute');
34076 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34077 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34079 if(b.size == 'md-left' || b.size == 'md-right'){
34080 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34081 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34084 b.el.setWidth(width);
34085 b.el.setHeight(height);
34087 b.el.select('iframe',true).setSize(width,height);
34091 for (var i = 0; i < this.cols; i++){
34093 if(maxY[i] < maxY[col]){
34098 col = Math.min(col, i);
34102 x = pos.x + col * (this.colWidth + this.padWidth);
34106 var positions = [];
34108 switch (box.length){
34110 positions = this.getVerticalOneBoxColPositions(x, y, box);
34113 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34116 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34119 positions = this.getVerticalFourBoxColPositions(x, y, box);
34125 Roo.each(box, function(b,kk){
34127 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34129 var sz = b.el.getSize();
34131 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34139 for (var i = 0; i < this.cols; i++){
34140 mY = Math.max(mY, maxY[i]);
34143 this.el.setHeight(mY - pos.y);
34147 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34149 // var pos = this.el.getBox(true);
34152 // var maxX = pos.right;
34154 // var maxHeight = 0;
34156 // Roo.each(items, function(item, k){
34160 // item.el.position('absolute');
34162 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34164 // item.el.setWidth(width);
34166 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34168 // item.el.setHeight(height);
34171 // item.el.setXY([x, y], isInstant ? false : true);
34173 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34176 // y = y + height + this.alternativePadWidth;
34178 // maxHeight = maxHeight + height + this.alternativePadWidth;
34182 // this.el.setHeight(maxHeight);
34186 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34188 var pos = this.el.getBox(true);
34193 var maxX = pos.right;
34195 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34197 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34199 Roo.each(queue, function(box, k){
34201 Roo.each(box, function(b, kk){
34203 b.el.position('absolute');
34205 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34206 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34208 if(b.size == 'md-left' || b.size == 'md-right'){
34209 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34210 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34213 b.el.setWidth(width);
34214 b.el.setHeight(height);
34222 var positions = [];
34224 switch (box.length){
34226 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34229 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34232 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34235 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34241 Roo.each(box, function(b,kk){
34243 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34245 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34253 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34255 Roo.each(eItems, function(b,k){
34257 b.size = (k == 0) ? 'sm' : 'xs';
34258 b.x = (k == 0) ? 2 : 1;
34259 b.y = (k == 0) ? 2 : 1;
34261 b.el.position('absolute');
34263 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34265 b.el.setWidth(width);
34267 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34269 b.el.setHeight(height);
34273 var positions = [];
34276 x : maxX - this.unitWidth * 2 - this.gutter,
34281 x : maxX - this.unitWidth,
34282 y : minY + (this.unitWidth + this.gutter) * 2
34286 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34290 Roo.each(eItems, function(b,k){
34292 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34298 getVerticalOneBoxColPositions : function(x, y, box)
34302 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34304 if(box[0].size == 'md-left'){
34308 if(box[0].size == 'md-right'){
34313 x : x + (this.unitWidth + this.gutter) * rand,
34320 getVerticalTwoBoxColPositions : function(x, y, box)
34324 if(box[0].size == 'xs'){
34328 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34332 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34346 x : x + (this.unitWidth + this.gutter) * 2,
34347 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34354 getVerticalThreeBoxColPositions : function(x, y, box)
34358 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34366 x : x + (this.unitWidth + this.gutter) * 1,
34371 x : x + (this.unitWidth + this.gutter) * 2,
34379 if(box[0].size == 'xs' && box[1].size == 'xs'){
34388 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34392 x : x + (this.unitWidth + this.gutter) * 1,
34406 x : x + (this.unitWidth + this.gutter) * 2,
34411 x : x + (this.unitWidth + this.gutter) * 2,
34412 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34419 getVerticalFourBoxColPositions : function(x, y, box)
34423 if(box[0].size == 'xs'){
34432 y : y + (this.unitHeight + this.gutter) * 1
34437 y : y + (this.unitHeight + this.gutter) * 2
34441 x : x + (this.unitWidth + this.gutter) * 1,
34455 x : x + (this.unitWidth + this.gutter) * 2,
34460 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34461 y : y + (this.unitHeight + this.gutter) * 1
34465 x : x + (this.unitWidth + this.gutter) * 2,
34466 y : y + (this.unitWidth + this.gutter) * 2
34473 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34477 if(box[0].size == 'md-left'){
34479 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34486 if(box[0].size == 'md-right'){
34488 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34489 y : minY + (this.unitWidth + this.gutter) * 1
34495 var rand = Math.floor(Math.random() * (4 - box[0].y));
34498 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34499 y : minY + (this.unitWidth + this.gutter) * rand
34506 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34510 if(box[0].size == 'xs'){
34513 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34518 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34519 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34527 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34532 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34533 y : minY + (this.unitWidth + this.gutter) * 2
34540 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34544 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34547 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34552 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34553 y : minY + (this.unitWidth + this.gutter) * 1
34557 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34558 y : minY + (this.unitWidth + this.gutter) * 2
34565 if(box[0].size == 'xs' && box[1].size == 'xs'){
34568 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34573 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34578 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34579 y : minY + (this.unitWidth + this.gutter) * 1
34587 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34592 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34593 y : minY + (this.unitWidth + this.gutter) * 2
34597 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34598 y : minY + (this.unitWidth + this.gutter) * 2
34605 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34609 if(box[0].size == 'xs'){
34612 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34617 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34622 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),
34627 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34628 y : minY + (this.unitWidth + this.gutter) * 1
34636 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34641 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34642 y : minY + (this.unitWidth + this.gutter) * 2
34646 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34647 y : minY + (this.unitWidth + this.gutter) * 2
34651 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),
34652 y : minY + (this.unitWidth + this.gutter) * 2
34660 * remove a Masonry Brick
34661 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34663 removeBrick : function(brick_id)
34669 for (var i = 0; i<this.bricks.length; i++) {
34670 if (this.bricks[i].id == brick_id) {
34671 this.bricks.splice(i,1);
34672 this.el.dom.removeChild(Roo.get(brick_id).dom);
34679 * adds a Masonry Brick
34680 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34682 addBrick : function(cfg)
34684 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34685 //this.register(cn);
34686 cn.parentId = this.id;
34687 cn.render(this.el);
34692 * register a Masonry Brick
34693 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34696 register : function(brick)
34698 this.bricks.push(brick);
34699 brick.masonryId = this.id;
34703 * clear all the Masonry Brick
34705 clearAll : function()
34708 //this.getChildContainer().dom.innerHTML = "";
34709 this.el.dom.innerHTML = '';
34712 getSelected : function()
34714 if (!this.selectedBrick) {
34718 return this.selectedBrick;
34722 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34726 * register a Masonry Layout
34727 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34730 register : function(layout)
34732 this.groups[layout.id] = layout;
34735 * fetch a Masonry Layout based on the masonry layout ID
34736 * @param {string} the masonry layout to add
34737 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34740 get: function(layout_id) {
34741 if (typeof(this.groups[layout_id]) == 'undefined') {
34744 return this.groups[layout_id] ;
34756 * http://masonry.desandro.com
34758 * The idea is to render all the bricks based on vertical width...
34760 * The original code extends 'outlayer' - we might need to use that....
34766 * @class Roo.bootstrap.LayoutMasonryAuto
34767 * @extends Roo.bootstrap.Component
34768 * Bootstrap Layout Masonry class
34771 * Create a new Element
34772 * @param {Object} config The config object
34775 Roo.bootstrap.LayoutMasonryAuto = function(config){
34776 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34779 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34782 * @cfg {Boolean} isFitWidth - resize the width..
34784 isFitWidth : false, // options..
34786 * @cfg {Boolean} isOriginLeft = left align?
34788 isOriginLeft : true,
34790 * @cfg {Boolean} isOriginTop = top align?
34792 isOriginTop : false,
34794 * @cfg {Boolean} isLayoutInstant = no animation?
34796 isLayoutInstant : false, // needed?
34798 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34800 isResizingContainer : true,
34802 * @cfg {Number} columnWidth width of the columns
34808 * @cfg {Number} maxCols maximum number of columns
34813 * @cfg {Number} padHeight padding below box..
34819 * @cfg {Boolean} isAutoInitial defalut true
34822 isAutoInitial : true,
34828 initialColumnWidth : 0,
34829 currentSize : null,
34831 colYs : null, // array.
34838 bricks: null, //CompositeElement
34839 cols : 0, // array?
34840 // element : null, // wrapped now this.el
34841 _isLayoutInited : null,
34844 getAutoCreate : function(){
34848 cls: 'blog-masonary-wrapper ' + this.cls,
34850 cls : 'mas-boxes masonary'
34857 getChildContainer: function( )
34859 if (this.boxesEl) {
34860 return this.boxesEl;
34863 this.boxesEl = this.el.select('.mas-boxes').first();
34865 return this.boxesEl;
34869 initEvents : function()
34873 if(this.isAutoInitial){
34874 Roo.log('hook children rendered');
34875 this.on('childrenrendered', function() {
34876 Roo.log('children rendered');
34883 initial : function()
34885 this.reloadItems();
34887 this.currentSize = this.el.getBox(true);
34889 /// was window resize... - let's see if this works..
34890 Roo.EventManager.onWindowResize(this.resize, this);
34892 if(!this.isAutoInitial){
34897 this.layout.defer(500,this);
34900 reloadItems: function()
34902 this.bricks = this.el.select('.masonry-brick', true);
34904 this.bricks.each(function(b) {
34905 //Roo.log(b.getSize());
34906 if (!b.attr('originalwidth')) {
34907 b.attr('originalwidth', b.getSize().width);
34912 Roo.log(this.bricks.elements.length);
34915 resize : function()
34918 var cs = this.el.getBox(true);
34920 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34921 Roo.log("no change in with or X");
34924 this.currentSize = cs;
34928 layout : function()
34931 this._resetLayout();
34932 //this._manageStamps();
34934 // don't animate first layout
34935 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34936 this.layoutItems( isInstant );
34938 // flag for initalized
34939 this._isLayoutInited = true;
34942 layoutItems : function( isInstant )
34944 //var items = this._getItemsForLayout( this.items );
34945 // original code supports filtering layout items.. we just ignore it..
34947 this._layoutItems( this.bricks , isInstant );
34949 this._postLayout();
34951 _layoutItems : function ( items , isInstant)
34953 //this.fireEvent( 'layout', this, items );
34956 if ( !items || !items.elements.length ) {
34957 // no items, emit event with empty array
34962 items.each(function(item) {
34963 Roo.log("layout item");
34965 // get x/y object from method
34966 var position = this._getItemLayoutPosition( item );
34968 position.item = item;
34969 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34970 queue.push( position );
34973 this._processLayoutQueue( queue );
34975 /** Sets position of item in DOM
34976 * @param {Element} item
34977 * @param {Number} x - horizontal position
34978 * @param {Number} y - vertical position
34979 * @param {Boolean} isInstant - disables transitions
34981 _processLayoutQueue : function( queue )
34983 for ( var i=0, len = queue.length; i < len; i++ ) {
34984 var obj = queue[i];
34985 obj.item.position('absolute');
34986 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34992 * Any logic you want to do after each layout,
34993 * i.e. size the container
34995 _postLayout : function()
34997 this.resizeContainer();
35000 resizeContainer : function()
35002 if ( !this.isResizingContainer ) {
35005 var size = this._getContainerSize();
35007 this.el.setSize(size.width,size.height);
35008 this.boxesEl.setSize(size.width,size.height);
35014 _resetLayout : function()
35016 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35017 this.colWidth = this.el.getWidth();
35018 //this.gutter = this.el.getWidth();
35020 this.measureColumns();
35026 this.colYs.push( 0 );
35032 measureColumns : function()
35034 this.getContainerWidth();
35035 // if columnWidth is 0, default to outerWidth of first item
35036 if ( !this.columnWidth ) {
35037 var firstItem = this.bricks.first();
35038 Roo.log(firstItem);
35039 this.columnWidth = this.containerWidth;
35040 if (firstItem && firstItem.attr('originalwidth') ) {
35041 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35043 // columnWidth fall back to item of first element
35044 Roo.log("set column width?");
35045 this.initialColumnWidth = this.columnWidth ;
35047 // if first elem has no width, default to size of container
35052 if (this.initialColumnWidth) {
35053 this.columnWidth = this.initialColumnWidth;
35058 // column width is fixed at the top - however if container width get's smaller we should
35061 // this bit calcs how man columns..
35063 var columnWidth = this.columnWidth += this.gutter;
35065 // calculate columns
35066 var containerWidth = this.containerWidth + this.gutter;
35068 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35069 // fix rounding errors, typically with gutters
35070 var excess = columnWidth - containerWidth % columnWidth;
35073 // if overshoot is less than a pixel, round up, otherwise floor it
35074 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35075 cols = Math[ mathMethod ]( cols );
35076 this.cols = Math.max( cols, 1 );
35077 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35079 // padding positioning..
35080 var totalColWidth = this.cols * this.columnWidth;
35081 var padavail = this.containerWidth - totalColWidth;
35082 // so for 2 columns - we need 3 'pads'
35084 var padNeeded = (1+this.cols) * this.padWidth;
35086 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35088 this.columnWidth += padExtra
35089 //this.padWidth = Math.floor(padavail / ( this.cols));
35091 // adjust colum width so that padding is fixed??
35093 // we have 3 columns ... total = width * 3
35094 // we have X left over... that should be used by
35096 //if (this.expandC) {
35104 getContainerWidth : function()
35106 /* // container is parent if fit width
35107 var container = this.isFitWidth ? this.element.parentNode : this.element;
35108 // check that this.size and size are there
35109 // IE8 triggers resize on body size change, so they might not be
35111 var size = getSize( container ); //FIXME
35112 this.containerWidth = size && size.innerWidth; //FIXME
35115 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35119 _getItemLayoutPosition : function( item ) // what is item?
35121 // we resize the item to our columnWidth..
35123 item.setWidth(this.columnWidth);
35124 item.autoBoxAdjust = false;
35126 var sz = item.getSize();
35128 // how many columns does this brick span
35129 var remainder = this.containerWidth % this.columnWidth;
35131 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35132 // round if off by 1 pixel, otherwise use ceil
35133 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35134 colSpan = Math.min( colSpan, this.cols );
35136 // normally this should be '1' as we dont' currently allow multi width columns..
35138 var colGroup = this._getColGroup( colSpan );
35139 // get the minimum Y value from the columns
35140 var minimumY = Math.min.apply( Math, colGroup );
35141 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35143 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35145 // position the brick
35147 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35148 y: this.currentSize.y + minimumY + this.padHeight
35152 // apply setHeight to necessary columns
35153 var setHeight = minimumY + sz.height + this.padHeight;
35154 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35156 var setSpan = this.cols + 1 - colGroup.length;
35157 for ( var i = 0; i < setSpan; i++ ) {
35158 this.colYs[ shortColIndex + i ] = setHeight ;
35165 * @param {Number} colSpan - number of columns the element spans
35166 * @returns {Array} colGroup
35168 _getColGroup : function( colSpan )
35170 if ( colSpan < 2 ) {
35171 // if brick spans only one column, use all the column Ys
35176 // how many different places could this brick fit horizontally
35177 var groupCount = this.cols + 1 - colSpan;
35178 // for each group potential horizontal position
35179 for ( var i = 0; i < groupCount; i++ ) {
35180 // make an array of colY values for that one group
35181 var groupColYs = this.colYs.slice( i, i + colSpan );
35182 // and get the max value of the array
35183 colGroup[i] = Math.max.apply( Math, groupColYs );
35188 _manageStamp : function( stamp )
35190 var stampSize = stamp.getSize();
35191 var offset = stamp.getBox();
35192 // get the columns that this stamp affects
35193 var firstX = this.isOriginLeft ? offset.x : offset.right;
35194 var lastX = firstX + stampSize.width;
35195 var firstCol = Math.floor( firstX / this.columnWidth );
35196 firstCol = Math.max( 0, firstCol );
35198 var lastCol = Math.floor( lastX / this.columnWidth );
35199 // lastCol should not go over if multiple of columnWidth #425
35200 lastCol -= lastX % this.columnWidth ? 0 : 1;
35201 lastCol = Math.min( this.cols - 1, lastCol );
35203 // set colYs to bottom of the stamp
35204 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35207 for ( var i = firstCol; i <= lastCol; i++ ) {
35208 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35213 _getContainerSize : function()
35215 this.maxY = Math.max.apply( Math, this.colYs );
35220 if ( this.isFitWidth ) {
35221 size.width = this._getContainerFitWidth();
35227 _getContainerFitWidth : function()
35229 var unusedCols = 0;
35230 // count unused columns
35233 if ( this.colYs[i] !== 0 ) {
35238 // fit container to columns that have been used
35239 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35242 needsResizeLayout : function()
35244 var previousWidth = this.containerWidth;
35245 this.getContainerWidth();
35246 return previousWidth !== this.containerWidth;
35261 * @class Roo.bootstrap.MasonryBrick
35262 * @extends Roo.bootstrap.Component
35263 * Bootstrap MasonryBrick class
35266 * Create a new MasonryBrick
35267 * @param {Object} config The config object
35270 Roo.bootstrap.MasonryBrick = function(config){
35272 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35274 Roo.bootstrap.MasonryBrick.register(this);
35280 * When a MasonryBrick is clcik
35281 * @param {Roo.bootstrap.MasonryBrick} this
35282 * @param {Roo.EventObject} e
35288 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35291 * @cfg {String} title
35295 * @cfg {String} html
35299 * @cfg {String} bgimage
35303 * @cfg {String} videourl
35307 * @cfg {String} cls
35311 * @cfg {String} href
35315 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35320 * @cfg {String} placetitle (center|bottom)
35325 * @cfg {Boolean} isFitContainer defalut true
35327 isFitContainer : true,
35330 * @cfg {Boolean} preventDefault defalut false
35332 preventDefault : false,
35335 * @cfg {Boolean} inverse defalut false
35337 maskInverse : false,
35339 getAutoCreate : function()
35341 if(!this.isFitContainer){
35342 return this.getSplitAutoCreate();
35345 var cls = 'masonry-brick masonry-brick-full';
35347 if(this.href.length){
35348 cls += ' masonry-brick-link';
35351 if(this.bgimage.length){
35352 cls += ' masonry-brick-image';
35355 if(this.maskInverse){
35356 cls += ' mask-inverse';
35359 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35360 cls += ' enable-mask';
35364 cls += ' masonry-' + this.size + '-brick';
35367 if(this.placetitle.length){
35369 switch (this.placetitle) {
35371 cls += ' masonry-center-title';
35374 cls += ' masonry-bottom-title';
35381 if(!this.html.length && !this.bgimage.length){
35382 cls += ' masonry-center-title';
35385 if(!this.html.length && this.bgimage.length){
35386 cls += ' masonry-bottom-title';
35391 cls += ' ' + this.cls;
35395 tag: (this.href.length) ? 'a' : 'div',
35400 cls: 'masonry-brick-mask'
35404 cls: 'masonry-brick-paragraph',
35410 if(this.href.length){
35411 cfg.href = this.href;
35414 var cn = cfg.cn[1].cn;
35416 if(this.title.length){
35419 cls: 'masonry-brick-title',
35424 if(this.html.length){
35427 cls: 'masonry-brick-text',
35432 if (!this.title.length && !this.html.length) {
35433 cfg.cn[1].cls += ' hide';
35436 if(this.bgimage.length){
35439 cls: 'masonry-brick-image-view',
35444 if(this.videourl.length){
35445 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35446 // youtube support only?
35449 cls: 'masonry-brick-image-view',
35452 allowfullscreen : true
35460 getSplitAutoCreate : function()
35462 var cls = 'masonry-brick masonry-brick-split';
35464 if(this.href.length){
35465 cls += ' masonry-brick-link';
35468 if(this.bgimage.length){
35469 cls += ' masonry-brick-image';
35473 cls += ' masonry-' + this.size + '-brick';
35476 switch (this.placetitle) {
35478 cls += ' masonry-center-title';
35481 cls += ' masonry-bottom-title';
35484 if(!this.bgimage.length){
35485 cls += ' masonry-center-title';
35488 if(this.bgimage.length){
35489 cls += ' masonry-bottom-title';
35495 cls += ' ' + this.cls;
35499 tag: (this.href.length) ? 'a' : 'div',
35504 cls: 'masonry-brick-split-head',
35508 cls: 'masonry-brick-paragraph',
35515 cls: 'masonry-brick-split-body',
35521 if(this.href.length){
35522 cfg.href = this.href;
35525 if(this.title.length){
35526 cfg.cn[0].cn[0].cn.push({
35528 cls: 'masonry-brick-title',
35533 if(this.html.length){
35534 cfg.cn[1].cn.push({
35536 cls: 'masonry-brick-text',
35541 if(this.bgimage.length){
35542 cfg.cn[0].cn.push({
35544 cls: 'masonry-brick-image-view',
35549 if(this.videourl.length){
35550 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35551 // youtube support only?
35552 cfg.cn[0].cn.cn.push({
35554 cls: 'masonry-brick-image-view',
35557 allowfullscreen : true
35564 initEvents: function()
35566 switch (this.size) {
35599 this.el.on('touchstart', this.onTouchStart, this);
35600 this.el.on('touchmove', this.onTouchMove, this);
35601 this.el.on('touchend', this.onTouchEnd, this);
35602 this.el.on('contextmenu', this.onContextMenu, this);
35604 this.el.on('mouseenter' ,this.enter, this);
35605 this.el.on('mouseleave', this.leave, this);
35606 this.el.on('click', this.onClick, this);
35609 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35610 this.parent().bricks.push(this);
35615 onClick: function(e, el)
35617 var time = this.endTimer - this.startTimer;
35618 // Roo.log(e.preventDefault());
35621 e.preventDefault();
35626 if(!this.preventDefault){
35630 e.preventDefault();
35632 if (this.activeClass != '') {
35633 this.selectBrick();
35636 this.fireEvent('click', this, e);
35639 enter: function(e, el)
35641 e.preventDefault();
35643 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35647 if(this.bgimage.length && this.html.length){
35648 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35652 leave: function(e, el)
35654 e.preventDefault();
35656 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35660 if(this.bgimage.length && this.html.length){
35661 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35665 onTouchStart: function(e, el)
35667 // e.preventDefault();
35669 this.touchmoved = false;
35671 if(!this.isFitContainer){
35675 if(!this.bgimage.length || !this.html.length){
35679 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35681 this.timer = new Date().getTime();
35685 onTouchMove: function(e, el)
35687 this.touchmoved = true;
35690 onContextMenu : function(e,el)
35692 e.preventDefault();
35693 e.stopPropagation();
35697 onTouchEnd: function(e, el)
35699 // e.preventDefault();
35701 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35708 if(!this.bgimage.length || !this.html.length){
35710 if(this.href.length){
35711 window.location.href = this.href;
35717 if(!this.isFitContainer){
35721 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35723 window.location.href = this.href;
35726 //selection on single brick only
35727 selectBrick : function() {
35729 if (!this.parentId) {
35733 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35734 var index = m.selectedBrick.indexOf(this.id);
35737 m.selectedBrick.splice(index,1);
35738 this.el.removeClass(this.activeClass);
35742 for(var i = 0; i < m.selectedBrick.length; i++) {
35743 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35744 b.el.removeClass(b.activeClass);
35747 m.selectedBrick = [];
35749 m.selectedBrick.push(this.id);
35750 this.el.addClass(this.activeClass);
35754 isSelected : function(){
35755 return this.el.hasClass(this.activeClass);
35760 Roo.apply(Roo.bootstrap.MasonryBrick, {
35763 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35765 * register a Masonry Brick
35766 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35769 register : function(brick)
35771 //this.groups[brick.id] = brick;
35772 this.groups.add(brick.id, brick);
35775 * fetch a masonry brick based on the masonry brick ID
35776 * @param {string} the masonry brick to add
35777 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35780 get: function(brick_id)
35782 // if (typeof(this.groups[brick_id]) == 'undefined') {
35785 // return this.groups[brick_id] ;
35787 if(this.groups.key(brick_id)) {
35788 return this.groups.key(brick_id);
35806 * @class Roo.bootstrap.Brick
35807 * @extends Roo.bootstrap.Component
35808 * Bootstrap Brick class
35811 * Create a new Brick
35812 * @param {Object} config The config object
35815 Roo.bootstrap.Brick = function(config){
35816 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35822 * When a Brick is click
35823 * @param {Roo.bootstrap.Brick} this
35824 * @param {Roo.EventObject} e
35830 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35833 * @cfg {String} title
35837 * @cfg {String} html
35841 * @cfg {String} bgimage
35845 * @cfg {String} cls
35849 * @cfg {String} href
35853 * @cfg {String} video
35857 * @cfg {Boolean} square
35861 getAutoCreate : function()
35863 var cls = 'roo-brick';
35865 if(this.href.length){
35866 cls += ' roo-brick-link';
35869 if(this.bgimage.length){
35870 cls += ' roo-brick-image';
35873 if(!this.html.length && !this.bgimage.length){
35874 cls += ' roo-brick-center-title';
35877 if(!this.html.length && this.bgimage.length){
35878 cls += ' roo-brick-bottom-title';
35882 cls += ' ' + this.cls;
35886 tag: (this.href.length) ? 'a' : 'div',
35891 cls: 'roo-brick-paragraph',
35897 if(this.href.length){
35898 cfg.href = this.href;
35901 var cn = cfg.cn[0].cn;
35903 if(this.title.length){
35906 cls: 'roo-brick-title',
35911 if(this.html.length){
35914 cls: 'roo-brick-text',
35921 if(this.bgimage.length){
35924 cls: 'roo-brick-image-view',
35932 initEvents: function()
35934 if(this.title.length || this.html.length){
35935 this.el.on('mouseenter' ,this.enter, this);
35936 this.el.on('mouseleave', this.leave, this);
35939 Roo.EventManager.onWindowResize(this.resize, this);
35941 if(this.bgimage.length){
35942 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35943 this.imageEl.on('load', this.onImageLoad, this);
35950 onImageLoad : function()
35955 resize : function()
35957 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35959 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35961 if(this.bgimage.length){
35962 var image = this.el.select('.roo-brick-image-view', true).first();
35964 image.setWidth(paragraph.getWidth());
35967 image.setHeight(paragraph.getWidth());
35970 this.el.setHeight(image.getHeight());
35971 paragraph.setHeight(image.getHeight());
35977 enter: function(e, el)
35979 e.preventDefault();
35981 if(this.bgimage.length){
35982 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35983 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35987 leave: function(e, el)
35989 e.preventDefault();
35991 if(this.bgimage.length){
35992 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35993 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36008 * @class Roo.bootstrap.NumberField
36009 * @extends Roo.bootstrap.Input
36010 * Bootstrap NumberField class
36016 * Create a new NumberField
36017 * @param {Object} config The config object
36020 Roo.bootstrap.NumberField = function(config){
36021 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36024 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36027 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36029 allowDecimals : true,
36031 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36033 decimalSeparator : ".",
36035 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36037 decimalPrecision : 2,
36039 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36041 allowNegative : true,
36044 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36048 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36050 minValue : Number.NEGATIVE_INFINITY,
36052 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36054 maxValue : Number.MAX_VALUE,
36056 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36058 minText : "The minimum value for this field is {0}",
36060 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36062 maxText : "The maximum value for this field is {0}",
36064 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36065 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36067 nanText : "{0} is not a valid number",
36069 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36071 thousandsDelimiter : false,
36073 * @cfg {String} valueAlign alignment of value
36075 valueAlign : "left",
36077 getAutoCreate : function()
36079 var hiddenInput = {
36083 cls: 'hidden-number-input'
36087 hiddenInput.name = this.name;
36092 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36094 this.name = hiddenInput.name;
36096 if(cfg.cn.length > 0) {
36097 cfg.cn.push(hiddenInput);
36104 initEvents : function()
36106 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36108 var allowed = "0123456789";
36110 if(this.allowDecimals){
36111 allowed += this.decimalSeparator;
36114 if(this.allowNegative){
36118 if(this.thousandsDelimiter) {
36122 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36124 var keyPress = function(e){
36126 var k = e.getKey();
36128 var c = e.getCharCode();
36131 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36132 allowed.indexOf(String.fromCharCode(c)) === -1
36138 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36142 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36147 this.el.on("keypress", keyPress, this);
36150 validateValue : function(value)
36153 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36157 var num = this.parseValue(value);
36160 this.markInvalid(String.format(this.nanText, value));
36164 if(num < this.minValue){
36165 this.markInvalid(String.format(this.minText, this.minValue));
36169 if(num > this.maxValue){
36170 this.markInvalid(String.format(this.maxText, this.maxValue));
36177 getValue : function()
36179 var v = this.hiddenEl().getValue();
36181 return this.fixPrecision(this.parseValue(v));
36184 parseValue : function(value)
36186 if(this.thousandsDelimiter) {
36188 r = new RegExp(",", "g");
36189 value = value.replace(r, "");
36192 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36193 return isNaN(value) ? '' : value;
36196 fixPrecision : function(value)
36198 if(this.thousandsDelimiter) {
36200 r = new RegExp(",", "g");
36201 value = value.replace(r, "");
36204 var nan = isNaN(value);
36206 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36207 return nan ? '' : value;
36209 return parseFloat(value).toFixed(this.decimalPrecision);
36212 setValue : function(v)
36214 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36220 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36222 this.inputEl().dom.value = (v == '') ? '' :
36223 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36225 if(!this.allowZero && v === '0') {
36226 this.hiddenEl().dom.value = '';
36227 this.inputEl().dom.value = '';
36234 decimalPrecisionFcn : function(v)
36236 return Math.floor(v);
36239 beforeBlur : function()
36241 var v = this.parseValue(this.getRawValue());
36243 if(v || v === 0 || v === ''){
36248 hiddenEl : function()
36250 return this.el.select('input.hidden-number-input',true).first();
36262 * @class Roo.bootstrap.DocumentSlider
36263 * @extends Roo.bootstrap.Component
36264 * Bootstrap DocumentSlider class
36267 * Create a new DocumentViewer
36268 * @param {Object} config The config object
36271 Roo.bootstrap.DocumentSlider = function(config){
36272 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36279 * Fire after initEvent
36280 * @param {Roo.bootstrap.DocumentSlider} this
36285 * Fire after update
36286 * @param {Roo.bootstrap.DocumentSlider} this
36292 * @param {Roo.bootstrap.DocumentSlider} this
36298 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36304 getAutoCreate : function()
36308 cls : 'roo-document-slider',
36312 cls : 'roo-document-slider-header',
36316 cls : 'roo-document-slider-header-title'
36322 cls : 'roo-document-slider-body',
36326 cls : 'roo-document-slider-prev',
36330 cls : 'fa fa-chevron-left'
36336 cls : 'roo-document-slider-thumb',
36340 cls : 'roo-document-slider-image'
36346 cls : 'roo-document-slider-next',
36350 cls : 'fa fa-chevron-right'
36362 initEvents : function()
36364 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36365 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36367 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36368 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36370 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36371 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36373 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36374 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36376 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36377 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36379 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36380 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36382 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36383 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36385 this.thumbEl.on('click', this.onClick, this);
36387 this.prevIndicator.on('click', this.prev, this);
36389 this.nextIndicator.on('click', this.next, this);
36393 initial : function()
36395 if(this.files.length){
36396 this.indicator = 1;
36400 this.fireEvent('initial', this);
36403 update : function()
36405 this.imageEl.attr('src', this.files[this.indicator - 1]);
36407 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36409 this.prevIndicator.show();
36411 if(this.indicator == 1){
36412 this.prevIndicator.hide();
36415 this.nextIndicator.show();
36417 if(this.indicator == this.files.length){
36418 this.nextIndicator.hide();
36421 this.thumbEl.scrollTo('top');
36423 this.fireEvent('update', this);
36426 onClick : function(e)
36428 e.preventDefault();
36430 this.fireEvent('click', this);
36435 e.preventDefault();
36437 this.indicator = Math.max(1, this.indicator - 1);
36444 e.preventDefault();
36446 this.indicator = Math.min(this.files.length, this.indicator + 1);
36460 * @class Roo.bootstrap.RadioSet
36461 * @extends Roo.bootstrap.Input
36462 * Bootstrap RadioSet class
36463 * @cfg {String} indicatorpos (left|right) default left
36464 * @cfg {Boolean} inline (true|false) inline the element (default true)
36465 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36467 * Create a new RadioSet
36468 * @param {Object} config The config object
36471 Roo.bootstrap.RadioSet = function(config){
36473 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36477 Roo.bootstrap.RadioSet.register(this);
36482 * Fires when the element is checked or unchecked.
36483 * @param {Roo.bootstrap.RadioSet} this This radio
36484 * @param {Roo.bootstrap.Radio} item The checked item
36489 * Fires when the element is click.
36490 * @param {Roo.bootstrap.RadioSet} this This radio set
36491 * @param {Roo.bootstrap.Radio} item The checked item
36492 * @param {Roo.EventObject} e The event object
36499 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36507 indicatorpos : 'left',
36509 getAutoCreate : function()
36513 cls : 'roo-radio-set-label',
36517 html : this.fieldLabel
36521 if (Roo.bootstrap.version == 3) {
36524 if(this.indicatorpos == 'left'){
36527 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36528 tooltip : 'This field is required'
36533 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36534 tooltip : 'This field is required'
36540 cls : 'roo-radio-set-items'
36543 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36545 if (align === 'left' && this.fieldLabel.length) {
36548 cls : "roo-radio-set-right",
36554 if(this.labelWidth > 12){
36555 label.style = "width: " + this.labelWidth + 'px';
36558 if(this.labelWidth < 13 && this.labelmd == 0){
36559 this.labelmd = this.labelWidth;
36562 if(this.labellg > 0){
36563 label.cls += ' col-lg-' + this.labellg;
36564 items.cls += ' col-lg-' + (12 - this.labellg);
36567 if(this.labelmd > 0){
36568 label.cls += ' col-md-' + this.labelmd;
36569 items.cls += ' col-md-' + (12 - this.labelmd);
36572 if(this.labelsm > 0){
36573 label.cls += ' col-sm-' + this.labelsm;
36574 items.cls += ' col-sm-' + (12 - this.labelsm);
36577 if(this.labelxs > 0){
36578 label.cls += ' col-xs-' + this.labelxs;
36579 items.cls += ' col-xs-' + (12 - this.labelxs);
36585 cls : 'roo-radio-set',
36589 cls : 'roo-radio-set-input',
36592 value : this.value ? this.value : ''
36599 if(this.weight.length){
36600 cfg.cls += ' roo-radio-' + this.weight;
36604 cfg.cls += ' roo-radio-set-inline';
36608 ['xs','sm','md','lg'].map(function(size){
36609 if (settings[size]) {
36610 cfg.cls += ' col-' + size + '-' + settings[size];
36618 initEvents : function()
36620 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36621 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36623 if(!this.fieldLabel.length){
36624 this.labelEl.hide();
36627 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36628 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36630 this.indicator = this.indicatorEl();
36632 if(this.indicator){
36633 this.indicator.addClass('invisible');
36636 this.originalValue = this.getValue();
36640 inputEl: function ()
36642 return this.el.select('.roo-radio-set-input', true).first();
36645 getChildContainer : function()
36647 return this.itemsEl;
36650 register : function(item)
36652 this.radioes.push(item);
36656 validate : function()
36658 if(this.getVisibilityEl().hasClass('hidden')){
36664 Roo.each(this.radioes, function(i){
36673 if(this.allowBlank) {
36677 if(this.disabled || valid){
36682 this.markInvalid();
36687 markValid : function()
36689 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36690 this.indicatorEl().removeClass('visible');
36691 this.indicatorEl().addClass('invisible');
36695 if (Roo.bootstrap.version == 3) {
36696 this.el.removeClass([this.invalidClass, this.validClass]);
36697 this.el.addClass(this.validClass);
36699 this.el.removeClass(['is-invalid','is-valid']);
36700 this.el.addClass(['is-valid']);
36702 this.fireEvent('valid', this);
36705 markInvalid : function(msg)
36707 if(this.allowBlank || this.disabled){
36711 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36712 this.indicatorEl().removeClass('invisible');
36713 this.indicatorEl().addClass('visible');
36715 if (Roo.bootstrap.version == 3) {
36716 this.el.removeClass([this.invalidClass, this.validClass]);
36717 this.el.addClass(this.invalidClass);
36719 this.el.removeClass(['is-invalid','is-valid']);
36720 this.el.addClass(['is-invalid']);
36723 this.fireEvent('invalid', this, msg);
36727 setValue : function(v, suppressEvent)
36729 if(this.value === v){
36736 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36739 Roo.each(this.radioes, function(i){
36741 i.el.removeClass('checked');
36744 Roo.each(this.radioes, function(i){
36746 if(i.value === v || i.value.toString() === v.toString()){
36748 i.el.addClass('checked');
36750 if(suppressEvent !== true){
36751 this.fireEvent('check', this, i);
36762 clearInvalid : function(){
36764 if(!this.el || this.preventMark){
36768 this.el.removeClass([this.invalidClass]);
36770 this.fireEvent('valid', this);
36775 Roo.apply(Roo.bootstrap.RadioSet, {
36779 register : function(set)
36781 this.groups[set.name] = set;
36784 get: function(name)
36786 if (typeof(this.groups[name]) == 'undefined') {
36790 return this.groups[name] ;
36796 * Ext JS Library 1.1.1
36797 * Copyright(c) 2006-2007, Ext JS, LLC.
36799 * Originally Released Under LGPL - original licence link has changed is not relivant.
36802 * <script type="text/javascript">
36807 * @class Roo.bootstrap.SplitBar
36808 * @extends Roo.util.Observable
36809 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36813 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36814 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36815 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36816 split.minSize = 100;
36817 split.maxSize = 600;
36818 split.animate = true;
36819 split.on('moved', splitterMoved);
36822 * Create a new SplitBar
36823 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36824 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36825 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36826 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36827 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36828 position of the SplitBar).
36830 Roo.bootstrap.SplitBar = function(cfg){
36835 // dragElement : elm
36836 // resizingElement: el,
36838 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36839 // placement : Roo.bootstrap.SplitBar.LEFT ,
36840 // existingProxy ???
36843 this.el = Roo.get(cfg.dragElement, true);
36844 this.el.dom.unselectable = "on";
36846 this.resizingEl = Roo.get(cfg.resizingElement, true);
36850 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36851 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36854 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36857 * The minimum size of the resizing element. (Defaults to 0)
36863 * The maximum size of the resizing element. (Defaults to 2000)
36866 this.maxSize = 2000;
36869 * Whether to animate the transition to the new size
36872 this.animate = false;
36875 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36878 this.useShim = false;
36883 if(!cfg.existingProxy){
36885 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36887 this.proxy = Roo.get(cfg.existingProxy).dom;
36890 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36893 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36896 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36899 this.dragSpecs = {};
36902 * @private The adapter to use to positon and resize elements
36904 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36905 this.adapter.init(this);
36907 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36909 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36910 this.el.addClass("roo-splitbar-h");
36913 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36914 this.el.addClass("roo-splitbar-v");
36920 * Fires when the splitter is moved (alias for {@link #event-moved})
36921 * @param {Roo.bootstrap.SplitBar} this
36922 * @param {Number} newSize the new width or height
36927 * Fires when the splitter is moved
36928 * @param {Roo.bootstrap.SplitBar} this
36929 * @param {Number} newSize the new width or height
36933 * @event beforeresize
36934 * Fires before the splitter is dragged
36935 * @param {Roo.bootstrap.SplitBar} this
36937 "beforeresize" : true,
36939 "beforeapply" : true
36942 Roo.util.Observable.call(this);
36945 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36946 onStartProxyDrag : function(x, y){
36947 this.fireEvent("beforeresize", this);
36949 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36951 o.enableDisplayMode("block");
36952 // all splitbars share the same overlay
36953 Roo.bootstrap.SplitBar.prototype.overlay = o;
36955 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36956 this.overlay.show();
36957 Roo.get(this.proxy).setDisplayed("block");
36958 var size = this.adapter.getElementSize(this);
36959 this.activeMinSize = this.getMinimumSize();;
36960 this.activeMaxSize = this.getMaximumSize();;
36961 var c1 = size - this.activeMinSize;
36962 var c2 = Math.max(this.activeMaxSize - size, 0);
36963 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36964 this.dd.resetConstraints();
36965 this.dd.setXConstraint(
36966 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36967 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36969 this.dd.setYConstraint(0, 0);
36971 this.dd.resetConstraints();
36972 this.dd.setXConstraint(0, 0);
36973 this.dd.setYConstraint(
36974 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36975 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36978 this.dragSpecs.startSize = size;
36979 this.dragSpecs.startPoint = [x, y];
36980 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36984 * @private Called after the drag operation by the DDProxy
36986 onEndProxyDrag : function(e){
36987 Roo.get(this.proxy).setDisplayed(false);
36988 var endPoint = Roo.lib.Event.getXY(e);
36990 this.overlay.hide();
36993 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36994 newSize = this.dragSpecs.startSize +
36995 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36996 endPoint[0] - this.dragSpecs.startPoint[0] :
36997 this.dragSpecs.startPoint[0] - endPoint[0]
37000 newSize = this.dragSpecs.startSize +
37001 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37002 endPoint[1] - this.dragSpecs.startPoint[1] :
37003 this.dragSpecs.startPoint[1] - endPoint[1]
37006 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37007 if(newSize != this.dragSpecs.startSize){
37008 if(this.fireEvent('beforeapply', this, newSize) !== false){
37009 this.adapter.setElementSize(this, newSize);
37010 this.fireEvent("moved", this, newSize);
37011 this.fireEvent("resize", this, newSize);
37017 * Get the adapter this SplitBar uses
37018 * @return The adapter object
37020 getAdapter : function(){
37021 return this.adapter;
37025 * Set the adapter this SplitBar uses
37026 * @param {Object} adapter A SplitBar adapter object
37028 setAdapter : function(adapter){
37029 this.adapter = adapter;
37030 this.adapter.init(this);
37034 * Gets the minimum size for the resizing element
37035 * @return {Number} The minimum size
37037 getMinimumSize : function(){
37038 return this.minSize;
37042 * Sets the minimum size for the resizing element
37043 * @param {Number} minSize The minimum size
37045 setMinimumSize : function(minSize){
37046 this.minSize = minSize;
37050 * Gets the maximum size for the resizing element
37051 * @return {Number} The maximum size
37053 getMaximumSize : function(){
37054 return this.maxSize;
37058 * Sets the maximum size for the resizing element
37059 * @param {Number} maxSize The maximum size
37061 setMaximumSize : function(maxSize){
37062 this.maxSize = maxSize;
37066 * Sets the initialize size for the resizing element
37067 * @param {Number} size The initial size
37069 setCurrentSize : function(size){
37070 var oldAnimate = this.animate;
37071 this.animate = false;
37072 this.adapter.setElementSize(this, size);
37073 this.animate = oldAnimate;
37077 * Destroy this splitbar.
37078 * @param {Boolean} removeEl True to remove the element
37080 destroy : function(removeEl){
37082 this.shim.remove();
37085 this.proxy.parentNode.removeChild(this.proxy);
37093 * @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.
37095 Roo.bootstrap.SplitBar.createProxy = function(dir){
37096 var proxy = new Roo.Element(document.createElement("div"));
37097 proxy.unselectable();
37098 var cls = 'roo-splitbar-proxy';
37099 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37100 document.body.appendChild(proxy.dom);
37105 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37106 * Default Adapter. It assumes the splitter and resizing element are not positioned
37107 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37109 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37112 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37113 // do nothing for now
37114 init : function(s){
37118 * Called before drag operations to get the current size of the resizing element.
37119 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37121 getElementSize : function(s){
37122 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37123 return s.resizingEl.getWidth();
37125 return s.resizingEl.getHeight();
37130 * Called after drag operations to set the size of the resizing element.
37131 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37132 * @param {Number} newSize The new size to set
37133 * @param {Function} onComplete A function to be invoked when resizing is complete
37135 setElementSize : function(s, newSize, onComplete){
37136 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37138 s.resizingEl.setWidth(newSize);
37140 onComplete(s, newSize);
37143 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37148 s.resizingEl.setHeight(newSize);
37150 onComplete(s, newSize);
37153 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37160 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37161 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37162 * Adapter that moves the splitter element to align with the resized sizing element.
37163 * Used with an absolute positioned SplitBar.
37164 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37165 * document.body, make sure you assign an id to the body element.
37167 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37168 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37169 this.container = Roo.get(container);
37172 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37173 init : function(s){
37174 this.basic.init(s);
37177 getElementSize : function(s){
37178 return this.basic.getElementSize(s);
37181 setElementSize : function(s, newSize, onComplete){
37182 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37185 moveSplitter : function(s){
37186 var yes = Roo.bootstrap.SplitBar;
37187 switch(s.placement){
37189 s.el.setX(s.resizingEl.getRight());
37192 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37195 s.el.setY(s.resizingEl.getBottom());
37198 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37205 * Orientation constant - Create a vertical SplitBar
37209 Roo.bootstrap.SplitBar.VERTICAL = 1;
37212 * Orientation constant - Create a horizontal SplitBar
37216 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37219 * Placement constant - The resizing element is to the left of the splitter element
37223 Roo.bootstrap.SplitBar.LEFT = 1;
37226 * Placement constant - The resizing element is to the right of the splitter element
37230 Roo.bootstrap.SplitBar.RIGHT = 2;
37233 * Placement constant - The resizing element is positioned above the splitter element
37237 Roo.bootstrap.SplitBar.TOP = 3;
37240 * Placement constant - The resizing element is positioned under splitter element
37244 Roo.bootstrap.SplitBar.BOTTOM = 4;
37245 Roo.namespace("Roo.bootstrap.layout");/*
37247 * Ext JS Library 1.1.1
37248 * Copyright(c) 2006-2007, Ext JS, LLC.
37250 * Originally Released Under LGPL - original licence link has changed is not relivant.
37253 * <script type="text/javascript">
37257 * @class Roo.bootstrap.layout.Manager
37258 * @extends Roo.bootstrap.Component
37259 * Base class for layout managers.
37261 Roo.bootstrap.layout.Manager = function(config)
37263 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37269 /** false to disable window resize monitoring @type Boolean */
37270 this.monitorWindowResize = true;
37275 * Fires when a layout is performed.
37276 * @param {Roo.LayoutManager} this
37280 * @event regionresized
37281 * Fires when the user resizes a region.
37282 * @param {Roo.LayoutRegion} region The resized region
37283 * @param {Number} newSize The new size (width for east/west, height for north/south)
37285 "regionresized" : true,
37287 * @event regioncollapsed
37288 * Fires when a region is collapsed.
37289 * @param {Roo.LayoutRegion} region The collapsed region
37291 "regioncollapsed" : true,
37293 * @event regionexpanded
37294 * Fires when a region is expanded.
37295 * @param {Roo.LayoutRegion} region The expanded region
37297 "regionexpanded" : true
37299 this.updating = false;
37302 this.el = Roo.get(config.el);
37308 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37313 monitorWindowResize : true,
37319 onRender : function(ct, position)
37322 this.el = Roo.get(ct);
37325 //this.fireEvent('render',this);
37329 initEvents: function()
37333 // ie scrollbar fix
37334 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37335 document.body.scroll = "no";
37336 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37337 this.el.position('relative');
37339 this.id = this.el.id;
37340 this.el.addClass("roo-layout-container");
37341 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37342 if(this.el.dom != document.body ) {
37343 this.el.on('resize', this.layout,this);
37344 this.el.on('show', this.layout,this);
37350 * Returns true if this layout is currently being updated
37351 * @return {Boolean}
37353 isUpdating : function(){
37354 return this.updating;
37358 * Suspend the LayoutManager from doing auto-layouts while
37359 * making multiple add or remove calls
37361 beginUpdate : function(){
37362 this.updating = true;
37366 * Restore auto-layouts and optionally disable the manager from performing a layout
37367 * @param {Boolean} noLayout true to disable a layout update
37369 endUpdate : function(noLayout){
37370 this.updating = false;
37376 layout: function(){
37380 onRegionResized : function(region, newSize){
37381 this.fireEvent("regionresized", region, newSize);
37385 onRegionCollapsed : function(region){
37386 this.fireEvent("regioncollapsed", region);
37389 onRegionExpanded : function(region){
37390 this.fireEvent("regionexpanded", region);
37394 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37395 * performs box-model adjustments.
37396 * @return {Object} The size as an object {width: (the width), height: (the height)}
37398 getViewSize : function()
37401 if(this.el.dom != document.body){
37402 size = this.el.getSize();
37404 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37406 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37407 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37412 * Returns the Element this layout is bound to.
37413 * @return {Roo.Element}
37415 getEl : function(){
37420 * Returns the specified region.
37421 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37422 * @return {Roo.LayoutRegion}
37424 getRegion : function(target){
37425 return this.regions[target.toLowerCase()];
37428 onWindowResize : function(){
37429 if(this.monitorWindowResize){
37436 * Ext JS Library 1.1.1
37437 * Copyright(c) 2006-2007, Ext JS, LLC.
37439 * Originally Released Under LGPL - original licence link has changed is not relivant.
37442 * <script type="text/javascript">
37445 * @class Roo.bootstrap.layout.Border
37446 * @extends Roo.bootstrap.layout.Manager
37447 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37448 * please see: examples/bootstrap/nested.html<br><br>
37450 <b>The container the layout is rendered into can be either the body element or any other element.
37451 If it is not the body element, the container needs to either be an absolute positioned element,
37452 or you will need to add "position:relative" to the css of the container. You will also need to specify
37453 the container size if it is not the body element.</b>
37456 * Create a new Border
37457 * @param {Object} config Configuration options
37459 Roo.bootstrap.layout.Border = function(config){
37460 config = config || {};
37461 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37465 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37466 if(config[region]){
37467 config[region].region = region;
37468 this.addRegion(config[region]);
37474 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37476 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37478 parent : false, // this might point to a 'nest' or a ???
37481 * Creates and adds a new region if it doesn't already exist.
37482 * @param {String} target The target region key (north, south, east, west or center).
37483 * @param {Object} config The regions config object
37484 * @return {BorderLayoutRegion} The new region
37486 addRegion : function(config)
37488 if(!this.regions[config.region]){
37489 var r = this.factory(config);
37490 this.bindRegion(r);
37492 return this.regions[config.region];
37496 bindRegion : function(r){
37497 this.regions[r.config.region] = r;
37499 r.on("visibilitychange", this.layout, this);
37500 r.on("paneladded", this.layout, this);
37501 r.on("panelremoved", this.layout, this);
37502 r.on("invalidated", this.layout, this);
37503 r.on("resized", this.onRegionResized, this);
37504 r.on("collapsed", this.onRegionCollapsed, this);
37505 r.on("expanded", this.onRegionExpanded, this);
37509 * Performs a layout update.
37511 layout : function()
37513 if(this.updating) {
37517 // render all the rebions if they have not been done alreayd?
37518 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37519 if(this.regions[region] && !this.regions[region].bodyEl){
37520 this.regions[region].onRender(this.el)
37524 var size = this.getViewSize();
37525 var w = size.width;
37526 var h = size.height;
37531 //var x = 0, y = 0;
37533 var rs = this.regions;
37534 var north = rs["north"];
37535 var south = rs["south"];
37536 var west = rs["west"];
37537 var east = rs["east"];
37538 var center = rs["center"];
37539 //if(this.hideOnLayout){ // not supported anymore
37540 //c.el.setStyle("display", "none");
37542 if(north && north.isVisible()){
37543 var b = north.getBox();
37544 var m = north.getMargins();
37545 b.width = w - (m.left+m.right);
37548 centerY = b.height + b.y + m.bottom;
37549 centerH -= centerY;
37550 north.updateBox(this.safeBox(b));
37552 if(south && south.isVisible()){
37553 var b = south.getBox();
37554 var m = south.getMargins();
37555 b.width = w - (m.left+m.right);
37557 var totalHeight = (b.height + m.top + m.bottom);
37558 b.y = h - totalHeight + m.top;
37559 centerH -= totalHeight;
37560 south.updateBox(this.safeBox(b));
37562 if(west && west.isVisible()){
37563 var b = west.getBox();
37564 var m = west.getMargins();
37565 b.height = centerH - (m.top+m.bottom);
37567 b.y = centerY + m.top;
37568 var totalWidth = (b.width + m.left + m.right);
37569 centerX += totalWidth;
37570 centerW -= totalWidth;
37571 west.updateBox(this.safeBox(b));
37573 if(east && east.isVisible()){
37574 var b = east.getBox();
37575 var m = east.getMargins();
37576 b.height = centerH - (m.top+m.bottom);
37577 var totalWidth = (b.width + m.left + m.right);
37578 b.x = w - totalWidth + m.left;
37579 b.y = centerY + m.top;
37580 centerW -= totalWidth;
37581 east.updateBox(this.safeBox(b));
37584 var m = center.getMargins();
37586 x: centerX + m.left,
37587 y: centerY + m.top,
37588 width: centerW - (m.left+m.right),
37589 height: centerH - (m.top+m.bottom)
37591 //if(this.hideOnLayout){
37592 //center.el.setStyle("display", "block");
37594 center.updateBox(this.safeBox(centerBox));
37597 this.fireEvent("layout", this);
37601 safeBox : function(box){
37602 box.width = Math.max(0, box.width);
37603 box.height = Math.max(0, box.height);
37608 * Adds a ContentPanel (or subclass) to this layout.
37609 * @param {String} target The target region key (north, south, east, west or center).
37610 * @param {Roo.ContentPanel} panel The panel to add
37611 * @return {Roo.ContentPanel} The added panel
37613 add : function(target, panel){
37615 target = target.toLowerCase();
37616 return this.regions[target].add(panel);
37620 * Remove a ContentPanel (or subclass) to this layout.
37621 * @param {String} target The target region key (north, south, east, west or center).
37622 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37623 * @return {Roo.ContentPanel} The removed panel
37625 remove : function(target, panel){
37626 target = target.toLowerCase();
37627 return this.regions[target].remove(panel);
37631 * Searches all regions for a panel with the specified id
37632 * @param {String} panelId
37633 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37635 findPanel : function(panelId){
37636 var rs = this.regions;
37637 for(var target in rs){
37638 if(typeof rs[target] != "function"){
37639 var p = rs[target].getPanel(panelId);
37649 * Searches all regions for a panel with the specified id and activates (shows) it.
37650 * @param {String/ContentPanel} panelId The panels id or the panel itself
37651 * @return {Roo.ContentPanel} The shown panel or null
37653 showPanel : function(panelId) {
37654 var rs = this.regions;
37655 for(var target in rs){
37656 var r = rs[target];
37657 if(typeof r != "function"){
37658 if(r.hasPanel(panelId)){
37659 return r.showPanel(panelId);
37667 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37668 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37671 restoreState : function(provider){
37673 provider = Roo.state.Manager;
37675 var sm = new Roo.LayoutStateManager();
37676 sm.init(this, provider);
37682 * Adds a xtype elements to the layout.
37686 xtype : 'ContentPanel',
37693 xtype : 'NestedLayoutPanel',
37699 items : [ ... list of content panels or nested layout panels.. ]
37703 * @param {Object} cfg Xtype definition of item to add.
37705 addxtype : function(cfg)
37707 // basically accepts a pannel...
37708 // can accept a layout region..!?!?
37709 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37712 // theory? children can only be panels??
37714 //if (!cfg.xtype.match(/Panel$/)) {
37719 if (typeof(cfg.region) == 'undefined') {
37720 Roo.log("Failed to add Panel, region was not set");
37724 var region = cfg.region;
37730 xitems = cfg.items;
37735 if ( region == 'center') {
37736 Roo.log("Center: " + cfg.title);
37742 case 'Content': // ContentPanel (el, cfg)
37743 case 'Scroll': // ContentPanel (el, cfg)
37745 cfg.autoCreate = cfg.autoCreate || true;
37746 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37748 // var el = this.el.createChild();
37749 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37752 this.add(region, ret);
37756 case 'TreePanel': // our new panel!
37757 cfg.el = this.el.createChild();
37758 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37759 this.add(region, ret);
37764 // create a new Layout (which is a Border Layout...
37766 var clayout = cfg.layout;
37767 clayout.el = this.el.createChild();
37768 clayout.items = clayout.items || [];
37772 // replace this exitems with the clayout ones..
37773 xitems = clayout.items;
37775 // force background off if it's in center...
37776 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37777 cfg.background = false;
37779 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37782 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37783 //console.log('adding nested layout panel ' + cfg.toSource());
37784 this.add(region, ret);
37785 nb = {}; /// find first...
37790 // needs grid and region
37792 //var el = this.getRegion(region).el.createChild();
37794 *var el = this.el.createChild();
37795 // create the grid first...
37796 cfg.grid.container = el;
37797 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37800 if (region == 'center' && this.active ) {
37801 cfg.background = false;
37804 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37806 this.add(region, ret);
37808 if (cfg.background) {
37809 // render grid on panel activation (if panel background)
37810 ret.on('activate', function(gp) {
37811 if (!gp.grid.rendered) {
37812 // gp.grid.render(el);
37816 // cfg.grid.render(el);
37822 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37823 // it was the old xcomponent building that caused this before.
37824 // espeically if border is the top element in the tree.
37834 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37836 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37837 this.add(region, ret);
37841 throw "Can not add '" + cfg.xtype + "' to Border";
37847 this.beginUpdate();
37851 Roo.each(xitems, function(i) {
37852 region = nb && i.region ? i.region : false;
37854 var add = ret.addxtype(i);
37857 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37858 if (!i.background) {
37859 abn[region] = nb[region] ;
37866 // make the last non-background panel active..
37867 //if (nb) { Roo.log(abn); }
37870 for(var r in abn) {
37871 region = this.getRegion(r);
37873 // tried using nb[r], but it does not work..
37875 region.showPanel(abn[r]);
37886 factory : function(cfg)
37889 var validRegions = Roo.bootstrap.layout.Border.regions;
37891 var target = cfg.region;
37894 var r = Roo.bootstrap.layout;
37898 return new r.North(cfg);
37900 return new r.South(cfg);
37902 return new r.East(cfg);
37904 return new r.West(cfg);
37906 return new r.Center(cfg);
37908 throw 'Layout region "'+target+'" not supported.';
37915 * Ext JS Library 1.1.1
37916 * Copyright(c) 2006-2007, Ext JS, LLC.
37918 * Originally Released Under LGPL - original licence link has changed is not relivant.
37921 * <script type="text/javascript">
37925 * @class Roo.bootstrap.layout.Basic
37926 * @extends Roo.util.Observable
37927 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37928 * and does not have a titlebar, tabs or any other features. All it does is size and position
37929 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37930 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37931 * @cfg {string} region the region that it inhabits..
37932 * @cfg {bool} skipConfig skip config?
37936 Roo.bootstrap.layout.Basic = function(config){
37938 this.mgr = config.mgr;
37940 this.position = config.region;
37942 var skipConfig = config.skipConfig;
37946 * @scope Roo.BasicLayoutRegion
37950 * @event beforeremove
37951 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37952 * @param {Roo.LayoutRegion} this
37953 * @param {Roo.ContentPanel} panel The panel
37954 * @param {Object} e The cancel event object
37956 "beforeremove" : true,
37958 * @event invalidated
37959 * Fires when the layout for this region is changed.
37960 * @param {Roo.LayoutRegion} this
37962 "invalidated" : true,
37964 * @event visibilitychange
37965 * Fires when this region is shown or hidden
37966 * @param {Roo.LayoutRegion} this
37967 * @param {Boolean} visibility true or false
37969 "visibilitychange" : true,
37971 * @event paneladded
37972 * Fires when a panel is added.
37973 * @param {Roo.LayoutRegion} this
37974 * @param {Roo.ContentPanel} panel The panel
37976 "paneladded" : true,
37978 * @event panelremoved
37979 * Fires when a panel is removed.
37980 * @param {Roo.LayoutRegion} this
37981 * @param {Roo.ContentPanel} panel The panel
37983 "panelremoved" : true,
37985 * @event beforecollapse
37986 * Fires when this region before collapse.
37987 * @param {Roo.LayoutRegion} this
37989 "beforecollapse" : true,
37992 * Fires when this region is collapsed.
37993 * @param {Roo.LayoutRegion} this
37995 "collapsed" : true,
37998 * Fires when this region is expanded.
37999 * @param {Roo.LayoutRegion} this
38004 * Fires when this region is slid into view.
38005 * @param {Roo.LayoutRegion} this
38007 "slideshow" : true,
38010 * Fires when this region slides out of view.
38011 * @param {Roo.LayoutRegion} this
38013 "slidehide" : true,
38015 * @event panelactivated
38016 * Fires when a panel is activated.
38017 * @param {Roo.LayoutRegion} this
38018 * @param {Roo.ContentPanel} panel The activated panel
38020 "panelactivated" : true,
38023 * Fires when the user resizes this region.
38024 * @param {Roo.LayoutRegion} this
38025 * @param {Number} newSize The new size (width for east/west, height for north/south)
38029 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38030 this.panels = new Roo.util.MixedCollection();
38031 this.panels.getKey = this.getPanelId.createDelegate(this);
38033 this.activePanel = null;
38034 // ensure listeners are added...
38036 if (config.listeners || config.events) {
38037 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38038 listeners : config.listeners || {},
38039 events : config.events || {}
38043 if(skipConfig !== true){
38044 this.applyConfig(config);
38048 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38050 getPanelId : function(p){
38054 applyConfig : function(config){
38055 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38056 this.config = config;
38061 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38062 * the width, for horizontal (north, south) the height.
38063 * @param {Number} newSize The new width or height
38065 resizeTo : function(newSize){
38066 var el = this.el ? this.el :
38067 (this.activePanel ? this.activePanel.getEl() : null);
38069 switch(this.position){
38072 el.setWidth(newSize);
38073 this.fireEvent("resized", this, newSize);
38077 el.setHeight(newSize);
38078 this.fireEvent("resized", this, newSize);
38084 getBox : function(){
38085 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38088 getMargins : function(){
38089 return this.margins;
38092 updateBox : function(box){
38094 var el = this.activePanel.getEl();
38095 el.dom.style.left = box.x + "px";
38096 el.dom.style.top = box.y + "px";
38097 this.activePanel.setSize(box.width, box.height);
38101 * Returns the container element for this region.
38102 * @return {Roo.Element}
38104 getEl : function(){
38105 return this.activePanel;
38109 * Returns true if this region is currently visible.
38110 * @return {Boolean}
38112 isVisible : function(){
38113 return this.activePanel ? true : false;
38116 setActivePanel : function(panel){
38117 panel = this.getPanel(panel);
38118 if(this.activePanel && this.activePanel != panel){
38119 this.activePanel.setActiveState(false);
38120 this.activePanel.getEl().setLeftTop(-10000,-10000);
38122 this.activePanel = panel;
38123 panel.setActiveState(true);
38125 panel.setSize(this.box.width, this.box.height);
38127 this.fireEvent("panelactivated", this, panel);
38128 this.fireEvent("invalidated");
38132 * Show the specified panel.
38133 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38134 * @return {Roo.ContentPanel} The shown panel or null
38136 showPanel : function(panel){
38137 panel = this.getPanel(panel);
38139 this.setActivePanel(panel);
38145 * Get the active panel for this region.
38146 * @return {Roo.ContentPanel} The active panel or null
38148 getActivePanel : function(){
38149 return this.activePanel;
38153 * Add the passed ContentPanel(s)
38154 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38155 * @return {Roo.ContentPanel} The panel added (if only one was added)
38157 add : function(panel){
38158 if(arguments.length > 1){
38159 for(var i = 0, len = arguments.length; i < len; i++) {
38160 this.add(arguments[i]);
38164 if(this.hasPanel(panel)){
38165 this.showPanel(panel);
38168 var el = panel.getEl();
38169 if(el.dom.parentNode != this.mgr.el.dom){
38170 this.mgr.el.dom.appendChild(el.dom);
38172 if(panel.setRegion){
38173 panel.setRegion(this);
38175 this.panels.add(panel);
38176 el.setStyle("position", "absolute");
38177 if(!panel.background){
38178 this.setActivePanel(panel);
38179 if(this.config.initialSize && this.panels.getCount()==1){
38180 this.resizeTo(this.config.initialSize);
38183 this.fireEvent("paneladded", this, panel);
38188 * Returns true if the panel is in this region.
38189 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38190 * @return {Boolean}
38192 hasPanel : function(panel){
38193 if(typeof panel == "object"){ // must be panel obj
38194 panel = panel.getId();
38196 return this.getPanel(panel) ? true : false;
38200 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38201 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38202 * @param {Boolean} preservePanel Overrides the config preservePanel option
38203 * @return {Roo.ContentPanel} The panel that was removed
38205 remove : function(panel, preservePanel){
38206 panel = this.getPanel(panel);
38211 this.fireEvent("beforeremove", this, panel, e);
38212 if(e.cancel === true){
38215 var panelId = panel.getId();
38216 this.panels.removeKey(panelId);
38221 * Returns the panel specified or null if it's not in this region.
38222 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38223 * @return {Roo.ContentPanel}
38225 getPanel : function(id){
38226 if(typeof id == "object"){ // must be panel obj
38229 return this.panels.get(id);
38233 * Returns this regions position (north/south/east/west/center).
38236 getPosition: function(){
38237 return this.position;
38241 * Ext JS Library 1.1.1
38242 * Copyright(c) 2006-2007, Ext JS, LLC.
38244 * Originally Released Under LGPL - original licence link has changed is not relivant.
38247 * <script type="text/javascript">
38251 * @class Roo.bootstrap.layout.Region
38252 * @extends Roo.bootstrap.layout.Basic
38253 * This class represents a region in a layout manager.
38255 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38256 * @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})
38257 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38258 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38259 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38260 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38261 * @cfg {String} title The title for the region (overrides panel titles)
38262 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38263 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38264 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38265 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38266 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38267 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38268 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38269 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38270 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38271 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38273 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38274 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38275 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38276 * @cfg {Number} width For East/West panels
38277 * @cfg {Number} height For North/South panels
38278 * @cfg {Boolean} split To show the splitter
38279 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38281 * @cfg {string} cls Extra CSS classes to add to region
38283 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38284 * @cfg {string} region the region that it inhabits..
38287 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38288 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38290 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38291 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38292 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38294 Roo.bootstrap.layout.Region = function(config)
38296 this.applyConfig(config);
38298 var mgr = config.mgr;
38299 var pos = config.region;
38300 config.skipConfig = true;
38301 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38304 this.onRender(mgr.el);
38307 this.visible = true;
38308 this.collapsed = false;
38309 this.unrendered_panels = [];
38312 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38314 position: '', // set by wrapper (eg. north/south etc..)
38315 unrendered_panels : null, // unrendered panels.
38317 tabPosition : false,
38319 mgr: false, // points to 'Border'
38322 createBody : function(){
38323 /** This region's body element
38324 * @type Roo.Element */
38325 this.bodyEl = this.el.createChild({
38327 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38331 onRender: function(ctr, pos)
38333 var dh = Roo.DomHelper;
38334 /** This region's container element
38335 * @type Roo.Element */
38336 this.el = dh.append(ctr.dom, {
38338 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38340 /** This region's title element
38341 * @type Roo.Element */
38343 this.titleEl = dh.append(this.el.dom, {
38345 unselectable: "on",
38346 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38348 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38349 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38353 this.titleEl.enableDisplayMode();
38354 /** This region's title text element
38355 * @type HTMLElement */
38356 this.titleTextEl = this.titleEl.dom.firstChild;
38357 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38359 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38360 this.closeBtn.enableDisplayMode();
38361 this.closeBtn.on("click", this.closeClicked, this);
38362 this.closeBtn.hide();
38364 this.createBody(this.config);
38365 if(this.config.hideWhenEmpty){
38367 this.on("paneladded", this.validateVisibility, this);
38368 this.on("panelremoved", this.validateVisibility, this);
38370 if(this.autoScroll){
38371 this.bodyEl.setStyle("overflow", "auto");
38373 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38375 //if(c.titlebar !== false){
38376 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38377 this.titleEl.hide();
38379 this.titleEl.show();
38380 if(this.config.title){
38381 this.titleTextEl.innerHTML = this.config.title;
38385 if(this.config.collapsed){
38386 this.collapse(true);
38388 if(this.config.hidden){
38392 if (this.unrendered_panels && this.unrendered_panels.length) {
38393 for (var i =0;i< this.unrendered_panels.length; i++) {
38394 this.add(this.unrendered_panels[i]);
38396 this.unrendered_panels = null;
38402 applyConfig : function(c)
38405 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38406 var dh = Roo.DomHelper;
38407 if(c.titlebar !== false){
38408 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38409 this.collapseBtn.on("click", this.collapse, this);
38410 this.collapseBtn.enableDisplayMode();
38412 if(c.showPin === true || this.showPin){
38413 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38414 this.stickBtn.enableDisplayMode();
38415 this.stickBtn.on("click", this.expand, this);
38416 this.stickBtn.hide();
38421 /** This region's collapsed element
38422 * @type Roo.Element */
38425 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38426 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38429 if(c.floatable !== false){
38430 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38431 this.collapsedEl.on("click", this.collapseClick, this);
38434 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38435 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38436 id: "message", unselectable: "on", style:{"float":"left"}});
38437 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38439 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38440 this.expandBtn.on("click", this.expand, this);
38444 if(this.collapseBtn){
38445 this.collapseBtn.setVisible(c.collapsible == true);
38448 this.cmargins = c.cmargins || this.cmargins ||
38449 (this.position == "west" || this.position == "east" ?
38450 {top: 0, left: 2, right:2, bottom: 0} :
38451 {top: 2, left: 0, right:0, bottom: 2});
38453 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38456 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38458 this.autoScroll = c.autoScroll || false;
38463 this.duration = c.duration || .30;
38464 this.slideDuration = c.slideDuration || .45;
38469 * Returns true if this region is currently visible.
38470 * @return {Boolean}
38472 isVisible : function(){
38473 return this.visible;
38477 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38478 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38480 //setCollapsedTitle : function(title){
38481 // title = title || " ";
38482 // if(this.collapsedTitleTextEl){
38483 // this.collapsedTitleTextEl.innerHTML = title;
38487 getBox : function(){
38489 // if(!this.collapsed){
38490 b = this.el.getBox(false, true);
38492 // b = this.collapsedEl.getBox(false, true);
38497 getMargins : function(){
38498 return this.margins;
38499 //return this.collapsed ? this.cmargins : this.margins;
38502 highlight : function(){
38503 this.el.addClass("x-layout-panel-dragover");
38506 unhighlight : function(){
38507 this.el.removeClass("x-layout-panel-dragover");
38510 updateBox : function(box)
38512 if (!this.bodyEl) {
38513 return; // not rendered yet..
38517 if(!this.collapsed){
38518 this.el.dom.style.left = box.x + "px";
38519 this.el.dom.style.top = box.y + "px";
38520 this.updateBody(box.width, box.height);
38522 this.collapsedEl.dom.style.left = box.x + "px";
38523 this.collapsedEl.dom.style.top = box.y + "px";
38524 this.collapsedEl.setSize(box.width, box.height);
38527 this.tabs.autoSizeTabs();
38531 updateBody : function(w, h)
38534 this.el.setWidth(w);
38535 w -= this.el.getBorderWidth("rl");
38536 if(this.config.adjustments){
38537 w += this.config.adjustments[0];
38540 if(h !== null && h > 0){
38541 this.el.setHeight(h);
38542 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38543 h -= this.el.getBorderWidth("tb");
38544 if(this.config.adjustments){
38545 h += this.config.adjustments[1];
38547 this.bodyEl.setHeight(h);
38549 h = this.tabs.syncHeight(h);
38552 if(this.panelSize){
38553 w = w !== null ? w : this.panelSize.width;
38554 h = h !== null ? h : this.panelSize.height;
38556 if(this.activePanel){
38557 var el = this.activePanel.getEl();
38558 w = w !== null ? w : el.getWidth();
38559 h = h !== null ? h : el.getHeight();
38560 this.panelSize = {width: w, height: h};
38561 this.activePanel.setSize(w, h);
38563 if(Roo.isIE && this.tabs){
38564 this.tabs.el.repaint();
38569 * Returns the container element for this region.
38570 * @return {Roo.Element}
38572 getEl : function(){
38577 * Hides this region.
38580 //if(!this.collapsed){
38581 this.el.dom.style.left = "-2000px";
38584 // this.collapsedEl.dom.style.left = "-2000px";
38585 // this.collapsedEl.hide();
38587 this.visible = false;
38588 this.fireEvent("visibilitychange", this, false);
38592 * Shows this region if it was previously hidden.
38595 //if(!this.collapsed){
38598 // this.collapsedEl.show();
38600 this.visible = true;
38601 this.fireEvent("visibilitychange", this, true);
38604 closeClicked : function(){
38605 if(this.activePanel){
38606 this.remove(this.activePanel);
38610 collapseClick : function(e){
38612 e.stopPropagation();
38615 e.stopPropagation();
38621 * Collapses this region.
38622 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38625 collapse : function(skipAnim, skipCheck = false){
38626 if(this.collapsed) {
38630 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38632 this.collapsed = true;
38634 this.split.el.hide();
38636 if(this.config.animate && skipAnim !== true){
38637 this.fireEvent("invalidated", this);
38638 this.animateCollapse();
38640 this.el.setLocation(-20000,-20000);
38642 this.collapsedEl.show();
38643 this.fireEvent("collapsed", this);
38644 this.fireEvent("invalidated", this);
38650 animateCollapse : function(){
38655 * Expands this region if it was previously collapsed.
38656 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38657 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38660 expand : function(e, skipAnim){
38662 e.stopPropagation();
38664 if(!this.collapsed || this.el.hasActiveFx()) {
38668 this.afterSlideIn();
38671 this.collapsed = false;
38672 if(this.config.animate && skipAnim !== true){
38673 this.animateExpand();
38677 this.split.el.show();
38679 this.collapsedEl.setLocation(-2000,-2000);
38680 this.collapsedEl.hide();
38681 this.fireEvent("invalidated", this);
38682 this.fireEvent("expanded", this);
38686 animateExpand : function(){
38690 initTabs : function()
38692 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38694 var ts = new Roo.bootstrap.panel.Tabs({
38695 el: this.bodyEl.dom,
38697 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38698 disableTooltips: this.config.disableTabTips,
38699 toolbar : this.config.toolbar
38702 if(this.config.hideTabs){
38703 ts.stripWrap.setDisplayed(false);
38706 ts.resizeTabs = this.config.resizeTabs === true;
38707 ts.minTabWidth = this.config.minTabWidth || 40;
38708 ts.maxTabWidth = this.config.maxTabWidth || 250;
38709 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38710 ts.monitorResize = false;
38711 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38712 ts.bodyEl.addClass('roo-layout-tabs-body');
38713 this.panels.each(this.initPanelAsTab, this);
38716 initPanelAsTab : function(panel){
38717 var ti = this.tabs.addTab(
38721 this.config.closeOnTab && panel.isClosable(),
38724 if(panel.tabTip !== undefined){
38725 ti.setTooltip(panel.tabTip);
38727 ti.on("activate", function(){
38728 this.setActivePanel(panel);
38731 if(this.config.closeOnTab){
38732 ti.on("beforeclose", function(t, e){
38734 this.remove(panel);
38738 panel.tabItem = ti;
38743 updatePanelTitle : function(panel, title)
38745 if(this.activePanel == panel){
38746 this.updateTitle(title);
38749 var ti = this.tabs.getTab(panel.getEl().id);
38751 if(panel.tabTip !== undefined){
38752 ti.setTooltip(panel.tabTip);
38757 updateTitle : function(title){
38758 if(this.titleTextEl && !this.config.title){
38759 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38763 setActivePanel : function(panel)
38765 panel = this.getPanel(panel);
38766 if(this.activePanel && this.activePanel != panel){
38767 if(this.activePanel.setActiveState(false) === false){
38771 this.activePanel = panel;
38772 panel.setActiveState(true);
38773 if(this.panelSize){
38774 panel.setSize(this.panelSize.width, this.panelSize.height);
38777 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38779 this.updateTitle(panel.getTitle());
38781 this.fireEvent("invalidated", this);
38783 this.fireEvent("panelactivated", this, panel);
38787 * Shows the specified panel.
38788 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38789 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38791 showPanel : function(panel)
38793 panel = this.getPanel(panel);
38796 var tab = this.tabs.getTab(panel.getEl().id);
38797 if(tab.isHidden()){
38798 this.tabs.unhideTab(tab.id);
38802 this.setActivePanel(panel);
38809 * Get the active panel for this region.
38810 * @return {Roo.ContentPanel} The active panel or null
38812 getActivePanel : function(){
38813 return this.activePanel;
38816 validateVisibility : function(){
38817 if(this.panels.getCount() < 1){
38818 this.updateTitle(" ");
38819 this.closeBtn.hide();
38822 if(!this.isVisible()){
38829 * Adds the passed ContentPanel(s) to this region.
38830 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38831 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38833 add : function(panel)
38835 if(arguments.length > 1){
38836 for(var i = 0, len = arguments.length; i < len; i++) {
38837 this.add(arguments[i]);
38842 // if we have not been rendered yet, then we can not really do much of this..
38843 if (!this.bodyEl) {
38844 this.unrendered_panels.push(panel);
38851 if(this.hasPanel(panel)){
38852 this.showPanel(panel);
38855 panel.setRegion(this);
38856 this.panels.add(panel);
38857 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38858 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38859 // and hide them... ???
38860 this.bodyEl.dom.appendChild(panel.getEl().dom);
38861 if(panel.background !== true){
38862 this.setActivePanel(panel);
38864 this.fireEvent("paneladded", this, panel);
38871 this.initPanelAsTab(panel);
38875 if(panel.background !== true){
38876 this.tabs.activate(panel.getEl().id);
38878 this.fireEvent("paneladded", this, panel);
38883 * Hides the tab for the specified panel.
38884 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38886 hidePanel : function(panel){
38887 if(this.tabs && (panel = this.getPanel(panel))){
38888 this.tabs.hideTab(panel.getEl().id);
38893 * Unhides the tab for a previously hidden panel.
38894 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38896 unhidePanel : function(panel){
38897 if(this.tabs && (panel = this.getPanel(panel))){
38898 this.tabs.unhideTab(panel.getEl().id);
38902 clearPanels : function(){
38903 while(this.panels.getCount() > 0){
38904 this.remove(this.panels.first());
38909 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38910 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38911 * @param {Boolean} preservePanel Overrides the config preservePanel option
38912 * @return {Roo.ContentPanel} The panel that was removed
38914 remove : function(panel, preservePanel)
38916 panel = this.getPanel(panel);
38921 this.fireEvent("beforeremove", this, panel, e);
38922 if(e.cancel === true){
38925 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38926 var panelId = panel.getId();
38927 this.panels.removeKey(panelId);
38929 document.body.appendChild(panel.getEl().dom);
38932 this.tabs.removeTab(panel.getEl().id);
38933 }else if (!preservePanel){
38934 this.bodyEl.dom.removeChild(panel.getEl().dom);
38936 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38937 var p = this.panels.first();
38938 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38939 tempEl.appendChild(p.getEl().dom);
38940 this.bodyEl.update("");
38941 this.bodyEl.dom.appendChild(p.getEl().dom);
38943 this.updateTitle(p.getTitle());
38945 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38946 this.setActivePanel(p);
38948 panel.setRegion(null);
38949 if(this.activePanel == panel){
38950 this.activePanel = null;
38952 if(this.config.autoDestroy !== false && preservePanel !== true){
38953 try{panel.destroy();}catch(e){}
38955 this.fireEvent("panelremoved", this, panel);
38960 * Returns the TabPanel component used by this region
38961 * @return {Roo.TabPanel}
38963 getTabs : function(){
38967 createTool : function(parentEl, className){
38968 var btn = Roo.DomHelper.append(parentEl, {
38970 cls: "x-layout-tools-button",
38973 cls: "roo-layout-tools-button-inner " + className,
38977 btn.addClassOnOver("roo-layout-tools-button-over");
38982 * Ext JS Library 1.1.1
38983 * Copyright(c) 2006-2007, Ext JS, LLC.
38985 * Originally Released Under LGPL - original licence link has changed is not relivant.
38988 * <script type="text/javascript">
38994 * @class Roo.SplitLayoutRegion
38995 * @extends Roo.LayoutRegion
38996 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38998 Roo.bootstrap.layout.Split = function(config){
38999 this.cursor = config.cursor;
39000 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39003 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39005 splitTip : "Drag to resize.",
39006 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39007 useSplitTips : false,
39009 applyConfig : function(config){
39010 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39013 onRender : function(ctr,pos) {
39015 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39016 if(!this.config.split){
39021 var splitEl = Roo.DomHelper.append(ctr.dom, {
39023 id: this.el.id + "-split",
39024 cls: "roo-layout-split roo-layout-split-"+this.position,
39027 /** The SplitBar for this region
39028 * @type Roo.SplitBar */
39029 // does not exist yet...
39030 Roo.log([this.position, this.orientation]);
39032 this.split = new Roo.bootstrap.SplitBar({
39033 dragElement : splitEl,
39034 resizingElement: this.el,
39035 orientation : this.orientation
39038 this.split.on("moved", this.onSplitMove, this);
39039 this.split.useShim = this.config.useShim === true;
39040 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39041 if(this.useSplitTips){
39042 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39044 //if(config.collapsible){
39045 // this.split.el.on("dblclick", this.collapse, this);
39048 if(typeof this.config.minSize != "undefined"){
39049 this.split.minSize = this.config.minSize;
39051 if(typeof this.config.maxSize != "undefined"){
39052 this.split.maxSize = this.config.maxSize;
39054 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39055 this.hideSplitter();
39060 getHMaxSize : function(){
39061 var cmax = this.config.maxSize || 10000;
39062 var center = this.mgr.getRegion("center");
39063 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39066 getVMaxSize : function(){
39067 var cmax = this.config.maxSize || 10000;
39068 var center = this.mgr.getRegion("center");
39069 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39072 onSplitMove : function(split, newSize){
39073 this.fireEvent("resized", this, newSize);
39077 * Returns the {@link Roo.SplitBar} for this region.
39078 * @return {Roo.SplitBar}
39080 getSplitBar : function(){
39085 this.hideSplitter();
39086 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39089 hideSplitter : function(){
39091 this.split.el.setLocation(-2000,-2000);
39092 this.split.el.hide();
39098 this.split.el.show();
39100 Roo.bootstrap.layout.Split.superclass.show.call(this);
39103 beforeSlide: function(){
39104 if(Roo.isGecko){// firefox overflow auto bug workaround
39105 this.bodyEl.clip();
39107 this.tabs.bodyEl.clip();
39109 if(this.activePanel){
39110 this.activePanel.getEl().clip();
39112 if(this.activePanel.beforeSlide){
39113 this.activePanel.beforeSlide();
39119 afterSlide : function(){
39120 if(Roo.isGecko){// firefox overflow auto bug workaround
39121 this.bodyEl.unclip();
39123 this.tabs.bodyEl.unclip();
39125 if(this.activePanel){
39126 this.activePanel.getEl().unclip();
39127 if(this.activePanel.afterSlide){
39128 this.activePanel.afterSlide();
39134 initAutoHide : function(){
39135 if(this.autoHide !== false){
39136 if(!this.autoHideHd){
39137 var st = new Roo.util.DelayedTask(this.slideIn, this);
39138 this.autoHideHd = {
39139 "mouseout": function(e){
39140 if(!e.within(this.el, true)){
39144 "mouseover" : function(e){
39150 this.el.on(this.autoHideHd);
39154 clearAutoHide : function(){
39155 if(this.autoHide !== false){
39156 this.el.un("mouseout", this.autoHideHd.mouseout);
39157 this.el.un("mouseover", this.autoHideHd.mouseover);
39161 clearMonitor : function(){
39162 Roo.get(document).un("click", this.slideInIf, this);
39165 // these names are backwards but not changed for compat
39166 slideOut : function(){
39167 if(this.isSlid || this.el.hasActiveFx()){
39170 this.isSlid = true;
39171 if(this.collapseBtn){
39172 this.collapseBtn.hide();
39174 this.closeBtnState = this.closeBtn.getStyle('display');
39175 this.closeBtn.hide();
39177 this.stickBtn.show();
39180 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39181 this.beforeSlide();
39182 this.el.setStyle("z-index", 10001);
39183 this.el.slideIn(this.getSlideAnchor(), {
39184 callback: function(){
39186 this.initAutoHide();
39187 Roo.get(document).on("click", this.slideInIf, this);
39188 this.fireEvent("slideshow", this);
39195 afterSlideIn : function(){
39196 this.clearAutoHide();
39197 this.isSlid = false;
39198 this.clearMonitor();
39199 this.el.setStyle("z-index", "");
39200 if(this.collapseBtn){
39201 this.collapseBtn.show();
39203 this.closeBtn.setStyle('display', this.closeBtnState);
39205 this.stickBtn.hide();
39207 this.fireEvent("slidehide", this);
39210 slideIn : function(cb){
39211 if(!this.isSlid || this.el.hasActiveFx()){
39215 this.isSlid = false;
39216 this.beforeSlide();
39217 this.el.slideOut(this.getSlideAnchor(), {
39218 callback: function(){
39219 this.el.setLeftTop(-10000, -10000);
39221 this.afterSlideIn();
39229 slideInIf : function(e){
39230 if(!e.within(this.el)){
39235 animateCollapse : function(){
39236 this.beforeSlide();
39237 this.el.setStyle("z-index", 20000);
39238 var anchor = this.getSlideAnchor();
39239 this.el.slideOut(anchor, {
39240 callback : function(){
39241 this.el.setStyle("z-index", "");
39242 this.collapsedEl.slideIn(anchor, {duration:.3});
39244 this.el.setLocation(-10000,-10000);
39246 this.fireEvent("collapsed", this);
39253 animateExpand : function(){
39254 this.beforeSlide();
39255 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39256 this.el.setStyle("z-index", 20000);
39257 this.collapsedEl.hide({
39260 this.el.slideIn(this.getSlideAnchor(), {
39261 callback : function(){
39262 this.el.setStyle("z-index", "");
39265 this.split.el.show();
39267 this.fireEvent("invalidated", this);
39268 this.fireEvent("expanded", this);
39296 getAnchor : function(){
39297 return this.anchors[this.position];
39300 getCollapseAnchor : function(){
39301 return this.canchors[this.position];
39304 getSlideAnchor : function(){
39305 return this.sanchors[this.position];
39308 getAlignAdj : function(){
39309 var cm = this.cmargins;
39310 switch(this.position){
39326 getExpandAdj : function(){
39327 var c = this.collapsedEl, cm = this.cmargins;
39328 switch(this.position){
39330 return [-(cm.right+c.getWidth()+cm.left), 0];
39333 return [cm.right+c.getWidth()+cm.left, 0];
39336 return [0, -(cm.top+cm.bottom+c.getHeight())];
39339 return [0, cm.top+cm.bottom+c.getHeight()];
39345 * Ext JS Library 1.1.1
39346 * Copyright(c) 2006-2007, Ext JS, LLC.
39348 * Originally Released Under LGPL - original licence link has changed is not relivant.
39351 * <script type="text/javascript">
39354 * These classes are private internal classes
39356 Roo.bootstrap.layout.Center = function(config){
39357 config.region = "center";
39358 Roo.bootstrap.layout.Region.call(this, config);
39359 this.visible = true;
39360 this.minWidth = config.minWidth || 20;
39361 this.minHeight = config.minHeight || 20;
39364 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39366 // center panel can't be hidden
39370 // center panel can't be hidden
39373 getMinWidth: function(){
39374 return this.minWidth;
39377 getMinHeight: function(){
39378 return this.minHeight;
39392 Roo.bootstrap.layout.North = function(config)
39394 config.region = 'north';
39395 config.cursor = 'n-resize';
39397 Roo.bootstrap.layout.Split.call(this, config);
39401 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39402 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39403 this.split.el.addClass("roo-layout-split-v");
39405 //var size = config.initialSize || config.height;
39406 //if(this.el && typeof size != "undefined"){
39407 // this.el.setHeight(size);
39410 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39412 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39415 onRender : function(ctr, pos)
39417 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39418 var size = this.config.initialSize || this.config.height;
39419 if(this.el && typeof size != "undefined"){
39420 this.el.setHeight(size);
39425 getBox : function(){
39426 if(this.collapsed){
39427 return this.collapsedEl.getBox();
39429 var box = this.el.getBox();
39431 box.height += this.split.el.getHeight();
39436 updateBox : function(box){
39437 if(this.split && !this.collapsed){
39438 box.height -= this.split.el.getHeight();
39439 this.split.el.setLeft(box.x);
39440 this.split.el.setTop(box.y+box.height);
39441 this.split.el.setWidth(box.width);
39443 if(this.collapsed){
39444 this.updateBody(box.width, null);
39446 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39454 Roo.bootstrap.layout.South = function(config){
39455 config.region = 'south';
39456 config.cursor = 's-resize';
39457 Roo.bootstrap.layout.Split.call(this, config);
39459 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39460 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39461 this.split.el.addClass("roo-layout-split-v");
39466 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39467 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39469 onRender : function(ctr, pos)
39471 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39472 var size = this.config.initialSize || this.config.height;
39473 if(this.el && typeof size != "undefined"){
39474 this.el.setHeight(size);
39479 getBox : function(){
39480 if(this.collapsed){
39481 return this.collapsedEl.getBox();
39483 var box = this.el.getBox();
39485 var sh = this.split.el.getHeight();
39492 updateBox : function(box){
39493 if(this.split && !this.collapsed){
39494 var sh = this.split.el.getHeight();
39497 this.split.el.setLeft(box.x);
39498 this.split.el.setTop(box.y-sh);
39499 this.split.el.setWidth(box.width);
39501 if(this.collapsed){
39502 this.updateBody(box.width, null);
39504 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39508 Roo.bootstrap.layout.East = function(config){
39509 config.region = "east";
39510 config.cursor = "e-resize";
39511 Roo.bootstrap.layout.Split.call(this, config);
39513 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39514 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39515 this.split.el.addClass("roo-layout-split-h");
39519 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39520 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39522 onRender : function(ctr, pos)
39524 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39525 var size = this.config.initialSize || this.config.width;
39526 if(this.el && typeof size != "undefined"){
39527 this.el.setWidth(size);
39532 getBox : function(){
39533 if(this.collapsed){
39534 return this.collapsedEl.getBox();
39536 var box = this.el.getBox();
39538 var sw = this.split.el.getWidth();
39545 updateBox : function(box){
39546 if(this.split && !this.collapsed){
39547 var sw = this.split.el.getWidth();
39549 this.split.el.setLeft(box.x);
39550 this.split.el.setTop(box.y);
39551 this.split.el.setHeight(box.height);
39554 if(this.collapsed){
39555 this.updateBody(null, box.height);
39557 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39561 Roo.bootstrap.layout.West = function(config){
39562 config.region = "west";
39563 config.cursor = "w-resize";
39565 Roo.bootstrap.layout.Split.call(this, config);
39567 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39568 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39569 this.split.el.addClass("roo-layout-split-h");
39573 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39574 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39576 onRender: function(ctr, pos)
39578 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39579 var size = this.config.initialSize || this.config.width;
39580 if(typeof size != "undefined"){
39581 this.el.setWidth(size);
39585 getBox : function(){
39586 if(this.collapsed){
39587 return this.collapsedEl.getBox();
39589 var box = this.el.getBox();
39590 if (box.width == 0) {
39591 box.width = this.config.width; // kludge?
39594 box.width += this.split.el.getWidth();
39599 updateBox : function(box){
39600 if(this.split && !this.collapsed){
39601 var sw = this.split.el.getWidth();
39603 this.split.el.setLeft(box.x+box.width);
39604 this.split.el.setTop(box.y);
39605 this.split.el.setHeight(box.height);
39607 if(this.collapsed){
39608 this.updateBody(null, box.height);
39610 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39612 });Roo.namespace("Roo.bootstrap.panel");/*
39614 * Ext JS Library 1.1.1
39615 * Copyright(c) 2006-2007, Ext JS, LLC.
39617 * Originally Released Under LGPL - original licence link has changed is not relivant.
39620 * <script type="text/javascript">
39623 * @class Roo.ContentPanel
39624 * @extends Roo.util.Observable
39625 * A basic ContentPanel element.
39626 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39627 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39628 * @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
39629 * @cfg {Boolean} closable True if the panel can be closed/removed
39630 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39631 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39632 * @cfg {Toolbar} toolbar A toolbar for this panel
39633 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39634 * @cfg {String} title The title for this panel
39635 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39636 * @cfg {String} url Calls {@link #setUrl} with this value
39637 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39638 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39639 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39640 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39641 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39642 * @cfg {Boolean} badges render the badges
39643 * @cfg {String} cls extra classes to use
39644 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39647 * Create a new ContentPanel.
39648 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39649 * @param {String/Object} config A string to set only the title or a config object
39650 * @param {String} content (optional) Set the HTML content for this panel
39651 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39653 Roo.bootstrap.panel.Content = function( config){
39655 this.tpl = config.tpl || false;
39657 var el = config.el;
39658 var content = config.content;
39660 if(config.autoCreate){ // xtype is available if this is called from factory
39663 this.el = Roo.get(el);
39664 if(!this.el && config && config.autoCreate){
39665 if(typeof config.autoCreate == "object"){
39666 if(!config.autoCreate.id){
39667 config.autoCreate.id = config.id||el;
39669 this.el = Roo.DomHelper.append(document.body,
39670 config.autoCreate, true);
39674 cls: (config.cls || '') +
39675 (config.background ? ' bg-' + config.background : '') +
39676 " roo-layout-inactive-content",
39679 if (config.iframe) {
39683 style : 'border: 0px',
39684 src : 'about:blank'
39690 elcfg.html = config.html;
39694 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39695 if (config.iframe) {
39696 this.iframeEl = this.el.select('iframe',true).first();
39701 this.closable = false;
39702 this.loaded = false;
39703 this.active = false;
39706 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39708 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39710 this.wrapEl = this.el; //this.el.wrap();
39712 if (config.toolbar.items) {
39713 ti = config.toolbar.items ;
39714 delete config.toolbar.items ;
39718 this.toolbar.render(this.wrapEl, 'before');
39719 for(var i =0;i < ti.length;i++) {
39720 // Roo.log(['add child', items[i]]);
39721 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39723 this.toolbar.items = nitems;
39724 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39725 delete config.toolbar;
39729 // xtype created footer. - not sure if will work as we normally have to render first..
39730 if (this.footer && !this.footer.el && this.footer.xtype) {
39731 if (!this.wrapEl) {
39732 this.wrapEl = this.el.wrap();
39735 this.footer.container = this.wrapEl.createChild();
39737 this.footer = Roo.factory(this.footer, Roo);
39742 if(typeof config == "string"){
39743 this.title = config;
39745 Roo.apply(this, config);
39749 this.resizeEl = Roo.get(this.resizeEl, true);
39751 this.resizeEl = this.el;
39753 // handle view.xtype
39761 * Fires when this panel is activated.
39762 * @param {Roo.ContentPanel} this
39766 * @event deactivate
39767 * Fires when this panel is activated.
39768 * @param {Roo.ContentPanel} this
39770 "deactivate" : true,
39774 * Fires when this panel is resized if fitToFrame is true.
39775 * @param {Roo.ContentPanel} this
39776 * @param {Number} width The width after any component adjustments
39777 * @param {Number} height The height after any component adjustments
39783 * Fires when this tab is created
39784 * @param {Roo.ContentPanel} this
39795 if(this.autoScroll && !this.iframe){
39796 this.resizeEl.setStyle("overflow", "auto");
39798 // fix randome scrolling
39799 //this.el.on('scroll', function() {
39800 // Roo.log('fix random scolling');
39801 // this.scrollTo('top',0);
39804 content = content || this.content;
39806 this.setContent(content);
39808 if(config && config.url){
39809 this.setUrl(this.url, this.params, this.loadOnce);
39814 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39816 if (this.view && typeof(this.view.xtype) != 'undefined') {
39817 this.view.el = this.el.appendChild(document.createElement("div"));
39818 this.view = Roo.factory(this.view);
39819 this.view.render && this.view.render(false, '');
39823 this.fireEvent('render', this);
39826 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39836 setRegion : function(region){
39837 this.region = region;
39838 this.setActiveClass(region && !this.background);
39842 setActiveClass: function(state)
39845 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39846 this.el.setStyle('position','relative');
39848 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39849 this.el.setStyle('position', 'absolute');
39854 * Returns the toolbar for this Panel if one was configured.
39855 * @return {Roo.Toolbar}
39857 getToolbar : function(){
39858 return this.toolbar;
39861 setActiveState : function(active)
39863 this.active = active;
39864 this.setActiveClass(active);
39866 if(this.fireEvent("deactivate", this) === false){
39871 this.fireEvent("activate", this);
39875 * Updates this panel's element (not for iframe)
39876 * @param {String} content The new content
39877 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39879 setContent : function(content, loadScripts){
39884 this.el.update(content, loadScripts);
39887 ignoreResize : function(w, h){
39888 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39891 this.lastSize = {width: w, height: h};
39896 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39897 * @return {Roo.UpdateManager} The UpdateManager
39899 getUpdateManager : function(){
39903 return this.el.getUpdateManager();
39906 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39907 * Does not work with IFRAME contents
39908 * @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:
39911 url: "your-url.php",
39912 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39913 callback: yourFunction,
39914 scope: yourObject, //(optional scope)
39917 text: "Loading...",
39923 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39924 * 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.
39925 * @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}
39926 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39927 * @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.
39928 * @return {Roo.ContentPanel} this
39936 var um = this.el.getUpdateManager();
39937 um.update.apply(um, arguments);
39943 * 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.
39944 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39945 * @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)
39946 * @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)
39947 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39949 setUrl : function(url, params, loadOnce){
39951 this.iframeEl.dom.src = url;
39955 if(this.refreshDelegate){
39956 this.removeListener("activate", this.refreshDelegate);
39958 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39959 this.on("activate", this.refreshDelegate);
39960 return this.el.getUpdateManager();
39963 _handleRefresh : function(url, params, loadOnce){
39964 if(!loadOnce || !this.loaded){
39965 var updater = this.el.getUpdateManager();
39966 updater.update(url, params, this._setLoaded.createDelegate(this));
39970 _setLoaded : function(){
39971 this.loaded = true;
39975 * Returns this panel's id
39978 getId : function(){
39983 * Returns this panel's element - used by regiosn to add.
39984 * @return {Roo.Element}
39986 getEl : function(){
39987 return this.wrapEl || this.el;
39992 adjustForComponents : function(width, height)
39994 //Roo.log('adjustForComponents ');
39995 if(this.resizeEl != this.el){
39996 width -= this.el.getFrameWidth('lr');
39997 height -= this.el.getFrameWidth('tb');
40000 var te = this.toolbar.getEl();
40001 te.setWidth(width);
40002 height -= te.getHeight();
40005 var te = this.footer.getEl();
40006 te.setWidth(width);
40007 height -= te.getHeight();
40011 if(this.adjustments){
40012 width += this.adjustments[0];
40013 height += this.adjustments[1];
40015 return {"width": width, "height": height};
40018 setSize : function(width, height){
40019 if(this.fitToFrame && !this.ignoreResize(width, height)){
40020 if(this.fitContainer && this.resizeEl != this.el){
40021 this.el.setSize(width, height);
40023 var size = this.adjustForComponents(width, height);
40025 this.iframeEl.setSize(width,height);
40028 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40029 this.fireEvent('resize', this, size.width, size.height);
40036 * Returns this panel's title
40039 getTitle : function(){
40041 if (typeof(this.title) != 'object') {
40046 for (var k in this.title) {
40047 if (!this.title.hasOwnProperty(k)) {
40051 if (k.indexOf('-') >= 0) {
40052 var s = k.split('-');
40053 for (var i = 0; i<s.length; i++) {
40054 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40057 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40064 * Set this panel's title
40065 * @param {String} title
40067 setTitle : function(title){
40068 this.title = title;
40070 this.region.updatePanelTitle(this, title);
40075 * Returns true is this panel was configured to be closable
40076 * @return {Boolean}
40078 isClosable : function(){
40079 return this.closable;
40082 beforeSlide : function(){
40084 this.resizeEl.clip();
40087 afterSlide : function(){
40089 this.resizeEl.unclip();
40093 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40094 * Will fail silently if the {@link #setUrl} method has not been called.
40095 * This does not activate the panel, just updates its content.
40097 refresh : function(){
40098 if(this.refreshDelegate){
40099 this.loaded = false;
40100 this.refreshDelegate();
40105 * Destroys this panel
40107 destroy : function(){
40108 this.el.removeAllListeners();
40109 var tempEl = document.createElement("span");
40110 tempEl.appendChild(this.el.dom);
40111 tempEl.innerHTML = "";
40117 * form - if the content panel contains a form - this is a reference to it.
40118 * @type {Roo.form.Form}
40122 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40123 * This contains a reference to it.
40129 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40139 * @param {Object} cfg Xtype definition of item to add.
40143 getChildContainer: function () {
40144 return this.getEl();
40149 var ret = new Roo.factory(cfg);
40154 if (cfg.xtype.match(/^Form$/)) {
40157 //if (this.footer) {
40158 // el = this.footer.container.insertSibling(false, 'before');
40160 el = this.el.createChild();
40163 this.form = new Roo.form.Form(cfg);
40166 if ( this.form.allItems.length) {
40167 this.form.render(el.dom);
40171 // should only have one of theses..
40172 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40173 // views.. should not be just added - used named prop 'view''
40175 cfg.el = this.el.appendChild(document.createElement("div"));
40178 var ret = new Roo.factory(cfg);
40180 ret.render && ret.render(false, ''); // render blank..
40190 * @class Roo.bootstrap.panel.Grid
40191 * @extends Roo.bootstrap.panel.Content
40193 * Create a new GridPanel.
40194 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40195 * @param {Object} config A the config object
40201 Roo.bootstrap.panel.Grid = function(config)
40205 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40206 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40208 config.el = this.wrapper;
40209 //this.el = this.wrapper;
40211 if (config.container) {
40212 // ctor'ed from a Border/panel.grid
40215 this.wrapper.setStyle("overflow", "hidden");
40216 this.wrapper.addClass('roo-grid-container');
40221 if(config.toolbar){
40222 var tool_el = this.wrapper.createChild();
40223 this.toolbar = Roo.factory(config.toolbar);
40225 if (config.toolbar.items) {
40226 ti = config.toolbar.items ;
40227 delete config.toolbar.items ;
40231 this.toolbar.render(tool_el);
40232 for(var i =0;i < ti.length;i++) {
40233 // Roo.log(['add child', items[i]]);
40234 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40236 this.toolbar.items = nitems;
40238 delete config.toolbar;
40241 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40242 config.grid.scrollBody = true;;
40243 config.grid.monitorWindowResize = false; // turn off autosizing
40244 config.grid.autoHeight = false;
40245 config.grid.autoWidth = false;
40247 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40249 if (config.background) {
40250 // render grid on panel activation (if panel background)
40251 this.on('activate', function(gp) {
40252 if (!gp.grid.rendered) {
40253 gp.grid.render(this.wrapper);
40254 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40259 this.grid.render(this.wrapper);
40260 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40263 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40264 // ??? needed ??? config.el = this.wrapper;
40269 // xtype created footer. - not sure if will work as we normally have to render first..
40270 if (this.footer && !this.footer.el && this.footer.xtype) {
40272 var ctr = this.grid.getView().getFooterPanel(true);
40273 this.footer.dataSource = this.grid.dataSource;
40274 this.footer = Roo.factory(this.footer, Roo);
40275 this.footer.render(ctr);
40285 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40286 getId : function(){
40287 return this.grid.id;
40291 * Returns the grid for this panel
40292 * @return {Roo.bootstrap.Table}
40294 getGrid : function(){
40298 setSize : function(width, height){
40299 if(!this.ignoreResize(width, height)){
40300 var grid = this.grid;
40301 var size = this.adjustForComponents(width, height);
40302 // tfoot is not a footer?
40305 var gridel = grid.getGridEl();
40306 gridel.setSize(size.width, size.height);
40308 var tbd = grid.getGridEl().select('tbody', true).first();
40309 var thd = grid.getGridEl().select('thead',true).first();
40310 var tbf= grid.getGridEl().select('tfoot', true).first();
40313 size.height -= tbf.getHeight();
40316 size.height -= thd.getHeight();
40319 tbd.setSize(size.width, size.height );
40320 // this is for the account management tab -seems to work there.
40321 var thd = grid.getGridEl().select('thead',true).first();
40323 // tbd.setSize(size.width, size.height - thd.getHeight());
40332 beforeSlide : function(){
40333 this.grid.getView().scroller.clip();
40336 afterSlide : function(){
40337 this.grid.getView().scroller.unclip();
40340 destroy : function(){
40341 this.grid.destroy();
40343 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40348 * @class Roo.bootstrap.panel.Nest
40349 * @extends Roo.bootstrap.panel.Content
40351 * Create a new Panel, that can contain a layout.Border.
40354 * @param {Roo.BorderLayout} layout The layout for this panel
40355 * @param {String/Object} config A string to set only the title or a config object
40357 Roo.bootstrap.panel.Nest = function(config)
40359 // construct with only one argument..
40360 /* FIXME - implement nicer consturctors
40361 if (layout.layout) {
40363 layout = config.layout;
40364 delete config.layout;
40366 if (layout.xtype && !layout.getEl) {
40367 // then layout needs constructing..
40368 layout = Roo.factory(layout, Roo);
40372 config.el = config.layout.getEl();
40374 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40376 config.layout.monitorWindowResize = false; // turn off autosizing
40377 this.layout = config.layout;
40378 this.layout.getEl().addClass("roo-layout-nested-layout");
40379 this.layout.parent = this;
40386 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40388 setSize : function(width, height){
40389 if(!this.ignoreResize(width, height)){
40390 var size = this.adjustForComponents(width, height);
40391 var el = this.layout.getEl();
40392 if (size.height < 1) {
40393 el.setWidth(size.width);
40395 el.setSize(size.width, size.height);
40397 var touch = el.dom.offsetWidth;
40398 this.layout.layout();
40399 // ie requires a double layout on the first pass
40400 if(Roo.isIE && !this.initialized){
40401 this.initialized = true;
40402 this.layout.layout();
40407 // activate all subpanels if not currently active..
40409 setActiveState : function(active){
40410 this.active = active;
40411 this.setActiveClass(active);
40414 this.fireEvent("deactivate", this);
40418 this.fireEvent("activate", this);
40419 // not sure if this should happen before or after..
40420 if (!this.layout) {
40421 return; // should not happen..
40424 for (var r in this.layout.regions) {
40425 reg = this.layout.getRegion(r);
40426 if (reg.getActivePanel()) {
40427 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40428 reg.setActivePanel(reg.getActivePanel());
40431 if (!reg.panels.length) {
40434 reg.showPanel(reg.getPanel(0));
40443 * Returns the nested BorderLayout for this panel
40444 * @return {Roo.BorderLayout}
40446 getLayout : function(){
40447 return this.layout;
40451 * Adds a xtype elements to the layout of the nested panel
40455 xtype : 'ContentPanel',
40462 xtype : 'NestedLayoutPanel',
40468 items : [ ... list of content panels or nested layout panels.. ]
40472 * @param {Object} cfg Xtype definition of item to add.
40474 addxtype : function(cfg) {
40475 return this.layout.addxtype(cfg);
40480 * Ext JS Library 1.1.1
40481 * Copyright(c) 2006-2007, Ext JS, LLC.
40483 * Originally Released Under LGPL - original licence link has changed is not relivant.
40486 * <script type="text/javascript">
40489 * @class Roo.TabPanel
40490 * @extends Roo.util.Observable
40491 * A lightweight tab container.
40495 // basic tabs 1, built from existing content
40496 var tabs = new Roo.TabPanel("tabs1");
40497 tabs.addTab("script", "View Script");
40498 tabs.addTab("markup", "View Markup");
40499 tabs.activate("script");
40501 // more advanced tabs, built from javascript
40502 var jtabs = new Roo.TabPanel("jtabs");
40503 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40505 // set up the UpdateManager
40506 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40507 var updater = tab2.getUpdateManager();
40508 updater.setDefaultUrl("ajax1.htm");
40509 tab2.on('activate', updater.refresh, updater, true);
40511 // Use setUrl for Ajax loading
40512 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40513 tab3.setUrl("ajax2.htm", null, true);
40516 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40519 jtabs.activate("jtabs-1");
40522 * Create a new TabPanel.
40523 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40524 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40526 Roo.bootstrap.panel.Tabs = function(config){
40528 * The container element for this TabPanel.
40529 * @type Roo.Element
40531 this.el = Roo.get(config.el);
40534 if(typeof config == "boolean"){
40535 this.tabPosition = config ? "bottom" : "top";
40537 Roo.apply(this, config);
40541 if(this.tabPosition == "bottom"){
40542 // if tabs are at the bottom = create the body first.
40543 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40544 this.el.addClass("roo-tabs-bottom");
40546 // next create the tabs holders
40548 if (this.tabPosition == "west"){
40550 var reg = this.region; // fake it..
40552 if (!reg.mgr.parent) {
40555 reg = reg.mgr.parent.region;
40557 Roo.log("got nest?");
40559 if (reg.mgr.getRegion('west')) {
40560 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40561 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40562 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40563 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40564 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40572 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40573 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40574 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40575 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40580 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40583 // finally - if tabs are at the top, then create the body last..
40584 if(this.tabPosition != "bottom"){
40585 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40586 * @type Roo.Element
40588 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40589 this.el.addClass("roo-tabs-top");
40593 this.bodyEl.setStyle("position", "relative");
40595 this.active = null;
40596 this.activateDelegate = this.activate.createDelegate(this);
40601 * Fires when the active tab changes
40602 * @param {Roo.TabPanel} this
40603 * @param {Roo.TabPanelItem} activePanel The new active tab
40607 * @event beforetabchange
40608 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40609 * @param {Roo.TabPanel} this
40610 * @param {Object} e Set cancel to true on this object to cancel the tab change
40611 * @param {Roo.TabPanelItem} tab The tab being changed to
40613 "beforetabchange" : true
40616 Roo.EventManager.onWindowResize(this.onResize, this);
40617 this.cpad = this.el.getPadding("lr");
40618 this.hiddenCount = 0;
40621 // toolbar on the tabbar support...
40622 if (this.toolbar) {
40623 alert("no toolbar support yet");
40624 this.toolbar = false;
40626 var tcfg = this.toolbar;
40627 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40628 this.toolbar = new Roo.Toolbar(tcfg);
40629 if (Roo.isSafari) {
40630 var tbl = tcfg.container.child('table', true);
40631 tbl.setAttribute('width', '100%');
40639 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40642 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40644 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40646 tabPosition : "top",
40648 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40650 currentTabWidth : 0,
40652 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40656 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40660 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40662 preferredTabWidth : 175,
40664 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40666 resizeTabs : false,
40668 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40670 monitorResize : true,
40672 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40674 toolbar : false, // set by caller..
40676 region : false, /// set by caller
40678 disableTooltips : true, // not used yet...
40681 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40682 * @param {String} id The id of the div to use <b>or create</b>
40683 * @param {String} text The text for the tab
40684 * @param {String} content (optional) Content to put in the TabPanelItem body
40685 * @param {Boolean} closable (optional) True to create a close icon on the tab
40686 * @return {Roo.TabPanelItem} The created TabPanelItem
40688 addTab : function(id, text, content, closable, tpl)
40690 var item = new Roo.bootstrap.panel.TabItem({
40694 closable : closable,
40697 this.addTabItem(item);
40699 item.setContent(content);
40705 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40706 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40707 * @return {Roo.TabPanelItem}
40709 getTab : function(id){
40710 return this.items[id];
40714 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40715 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40717 hideTab : function(id){
40718 var t = this.items[id];
40721 this.hiddenCount++;
40722 this.autoSizeTabs();
40727 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40728 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40730 unhideTab : function(id){
40731 var t = this.items[id];
40733 t.setHidden(false);
40734 this.hiddenCount--;
40735 this.autoSizeTabs();
40740 * Adds an existing {@link Roo.TabPanelItem}.
40741 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40743 addTabItem : function(item)
40745 this.items[item.id] = item;
40746 this.items.push(item);
40747 this.autoSizeTabs();
40748 // if(this.resizeTabs){
40749 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40750 // this.autoSizeTabs();
40752 // item.autoSize();
40757 * Removes a {@link Roo.TabPanelItem}.
40758 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40760 removeTab : function(id){
40761 var items = this.items;
40762 var tab = items[id];
40763 if(!tab) { return; }
40764 var index = items.indexOf(tab);
40765 if(this.active == tab && items.length > 1){
40766 var newTab = this.getNextAvailable(index);
40771 this.stripEl.dom.removeChild(tab.pnode.dom);
40772 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40773 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40775 items.splice(index, 1);
40776 delete this.items[tab.id];
40777 tab.fireEvent("close", tab);
40778 tab.purgeListeners();
40779 this.autoSizeTabs();
40782 getNextAvailable : function(start){
40783 var items = this.items;
40785 // look for a next tab that will slide over to
40786 // replace the one being removed
40787 while(index < items.length){
40788 var item = items[++index];
40789 if(item && !item.isHidden()){
40793 // if one isn't found select the previous tab (on the left)
40796 var item = items[--index];
40797 if(item && !item.isHidden()){
40805 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40806 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40808 disableTab : function(id){
40809 var tab = this.items[id];
40810 if(tab && this.active != tab){
40816 * Enables a {@link Roo.TabPanelItem} that is disabled.
40817 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40819 enableTab : function(id){
40820 var tab = this.items[id];
40825 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40826 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40827 * @return {Roo.TabPanelItem} The TabPanelItem.
40829 activate : function(id)
40831 //Roo.log('activite:' + id);
40833 var tab = this.items[id];
40837 if(tab == this.active || tab.disabled){
40841 this.fireEvent("beforetabchange", this, e, tab);
40842 if(e.cancel !== true && !tab.disabled){
40844 this.active.hide();
40846 this.active = this.items[id];
40847 this.active.show();
40848 this.fireEvent("tabchange", this, this.active);
40854 * Gets the active {@link Roo.TabPanelItem}.
40855 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40857 getActiveTab : function(){
40858 return this.active;
40862 * Updates the tab body element to fit the height of the container element
40863 * for overflow scrolling
40864 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40866 syncHeight : function(targetHeight){
40867 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40868 var bm = this.bodyEl.getMargins();
40869 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40870 this.bodyEl.setHeight(newHeight);
40874 onResize : function(){
40875 if(this.monitorResize){
40876 this.autoSizeTabs();
40881 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40883 beginUpdate : function(){
40884 this.updating = true;
40888 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40890 endUpdate : function(){
40891 this.updating = false;
40892 this.autoSizeTabs();
40896 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40898 autoSizeTabs : function()
40900 var count = this.items.length;
40901 var vcount = count - this.hiddenCount;
40904 this.stripEl.hide();
40906 this.stripEl.show();
40909 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40914 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40915 var availWidth = Math.floor(w / vcount);
40916 var b = this.stripBody;
40917 if(b.getWidth() > w){
40918 var tabs = this.items;
40919 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40920 if(availWidth < this.minTabWidth){
40921 /*if(!this.sleft){ // incomplete scrolling code
40922 this.createScrollButtons();
40925 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40928 if(this.currentTabWidth < this.preferredTabWidth){
40929 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40935 * Returns the number of tabs in this TabPanel.
40938 getCount : function(){
40939 return this.items.length;
40943 * Resizes all the tabs to the passed width
40944 * @param {Number} The new width
40946 setTabWidth : function(width){
40947 this.currentTabWidth = width;
40948 for(var i = 0, len = this.items.length; i < len; i++) {
40949 if(!this.items[i].isHidden()) {
40950 this.items[i].setWidth(width);
40956 * Destroys this TabPanel
40957 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40959 destroy : function(removeEl){
40960 Roo.EventManager.removeResizeListener(this.onResize, this);
40961 for(var i = 0, len = this.items.length; i < len; i++){
40962 this.items[i].purgeListeners();
40964 if(removeEl === true){
40965 this.el.update("");
40970 createStrip : function(container)
40972 var strip = document.createElement("nav");
40973 strip.className = Roo.bootstrap.version == 4 ?
40974 "navbar-light bg-light" :
40975 "navbar navbar-default"; //"x-tabs-wrap";
40976 container.appendChild(strip);
40980 createStripList : function(strip)
40982 // div wrapper for retard IE
40983 // returns the "tr" element.
40984 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40985 //'<div class="x-tabs-strip-wrap">'+
40986 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40987 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40988 return strip.firstChild; //.firstChild.firstChild.firstChild;
40990 createBody : function(container)
40992 var body = document.createElement("div");
40993 Roo.id(body, "tab-body");
40994 //Roo.fly(body).addClass("x-tabs-body");
40995 Roo.fly(body).addClass("tab-content");
40996 container.appendChild(body);
40999 createItemBody :function(bodyEl, id){
41000 var body = Roo.getDom(id);
41002 body = document.createElement("div");
41005 //Roo.fly(body).addClass("x-tabs-item-body");
41006 Roo.fly(body).addClass("tab-pane");
41007 bodyEl.insertBefore(body, bodyEl.firstChild);
41011 createStripElements : function(stripEl, text, closable, tpl)
41013 var td = document.createElement("li"); // was td..
41014 td.className = 'nav-item';
41016 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41019 stripEl.appendChild(td);
41021 td.className = "x-tabs-closable";
41022 if(!this.closeTpl){
41023 this.closeTpl = new Roo.Template(
41024 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41025 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41026 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41029 var el = this.closeTpl.overwrite(td, {"text": text});
41030 var close = el.getElementsByTagName("div")[0];
41031 var inner = el.getElementsByTagName("em")[0];
41032 return {"el": el, "close": close, "inner": inner};
41035 // not sure what this is..
41036 // if(!this.tabTpl){
41037 //this.tabTpl = new Roo.Template(
41038 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41039 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41041 // this.tabTpl = new Roo.Template(
41042 // '<a href="#">' +
41043 // '<span unselectable="on"' +
41044 // (this.disableTooltips ? '' : ' title="{text}"') +
41045 // ' >{text}</span></a>'
41051 var template = tpl || this.tabTpl || false;
41054 template = new Roo.Template(
41055 Roo.bootstrap.version == 4 ?
41057 '<a class="nav-link" href="#" unselectable="on"' +
41058 (this.disableTooltips ? '' : ' title="{text}"') +
41061 '<a class="nav-link" href="#">' +
41062 '<span unselectable="on"' +
41063 (this.disableTooltips ? '' : ' title="{text}"') +
41064 ' >{text}</span></a>'
41069 switch (typeof(template)) {
41073 template = new Roo.Template(template);
41079 var el = template.overwrite(td, {"text": text});
41081 var inner = el.getElementsByTagName("span")[0];
41083 return {"el": el, "inner": inner};
41091 * @class Roo.TabPanelItem
41092 * @extends Roo.util.Observable
41093 * Represents an individual item (tab plus body) in a TabPanel.
41094 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41095 * @param {String} id The id of this TabPanelItem
41096 * @param {String} text The text for the tab of this TabPanelItem
41097 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41099 Roo.bootstrap.panel.TabItem = function(config){
41101 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41102 * @type Roo.TabPanel
41104 this.tabPanel = config.panel;
41106 * The id for this TabPanelItem
41109 this.id = config.id;
41111 this.disabled = false;
41113 this.text = config.text;
41115 this.loaded = false;
41116 this.closable = config.closable;
41119 * The body element for this TabPanelItem.
41120 * @type Roo.Element
41122 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41123 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41124 this.bodyEl.setStyle("display", "block");
41125 this.bodyEl.setStyle("zoom", "1");
41126 //this.hideAction();
41128 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41130 this.el = Roo.get(els.el);
41131 this.inner = Roo.get(els.inner, true);
41132 this.textEl = Roo.bootstrap.version == 4 ?
41133 this.el : Roo.get(this.el.dom.firstChild, true);
41135 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41136 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41139 // this.el.on("mousedown", this.onTabMouseDown, this);
41140 this.el.on("click", this.onTabClick, this);
41142 if(config.closable){
41143 var c = Roo.get(els.close, true);
41144 c.dom.title = this.closeText;
41145 c.addClassOnOver("close-over");
41146 c.on("click", this.closeClick, this);
41152 * Fires when this tab becomes the active tab.
41153 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41154 * @param {Roo.TabPanelItem} this
41158 * @event beforeclose
41159 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41160 * @param {Roo.TabPanelItem} this
41161 * @param {Object} e Set cancel to true on this object to cancel the close.
41163 "beforeclose": true,
41166 * Fires when this tab is closed.
41167 * @param {Roo.TabPanelItem} this
41171 * @event deactivate
41172 * Fires when this tab is no longer the active tab.
41173 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41174 * @param {Roo.TabPanelItem} this
41176 "deactivate" : true
41178 this.hidden = false;
41180 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41183 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41185 purgeListeners : function(){
41186 Roo.util.Observable.prototype.purgeListeners.call(this);
41187 this.el.removeAllListeners();
41190 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41193 this.status_node.addClass("active");
41196 this.tabPanel.stripWrap.repaint();
41198 this.fireEvent("activate", this.tabPanel, this);
41202 * Returns true if this tab is the active tab.
41203 * @return {Boolean}
41205 isActive : function(){
41206 return this.tabPanel.getActiveTab() == this;
41210 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41213 this.status_node.removeClass("active");
41215 this.fireEvent("deactivate", this.tabPanel, this);
41218 hideAction : function(){
41219 this.bodyEl.hide();
41220 this.bodyEl.setStyle("position", "absolute");
41221 this.bodyEl.setLeft("-20000px");
41222 this.bodyEl.setTop("-20000px");
41225 showAction : function(){
41226 this.bodyEl.setStyle("position", "relative");
41227 this.bodyEl.setTop("");
41228 this.bodyEl.setLeft("");
41229 this.bodyEl.show();
41233 * Set the tooltip for the tab.
41234 * @param {String} tooltip The tab's tooltip
41236 setTooltip : function(text){
41237 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41238 this.textEl.dom.qtip = text;
41239 this.textEl.dom.removeAttribute('title');
41241 this.textEl.dom.title = text;
41245 onTabClick : function(e){
41246 e.preventDefault();
41247 this.tabPanel.activate(this.id);
41250 onTabMouseDown : function(e){
41251 e.preventDefault();
41252 this.tabPanel.activate(this.id);
41255 getWidth : function(){
41256 return this.inner.getWidth();
41259 setWidth : function(width){
41260 var iwidth = width - this.linode.getPadding("lr");
41261 this.inner.setWidth(iwidth);
41262 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41263 this.linode.setWidth(width);
41267 * Show or hide the tab
41268 * @param {Boolean} hidden True to hide or false to show.
41270 setHidden : function(hidden){
41271 this.hidden = hidden;
41272 this.linode.setStyle("display", hidden ? "none" : "");
41276 * Returns true if this tab is "hidden"
41277 * @return {Boolean}
41279 isHidden : function(){
41280 return this.hidden;
41284 * Returns the text for this tab
41287 getText : function(){
41291 autoSize : function(){
41292 //this.el.beginMeasure();
41293 this.textEl.setWidth(1);
41295 * #2804 [new] Tabs in Roojs
41296 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41298 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41299 //this.el.endMeasure();
41303 * Sets the text for the tab (Note: this also sets the tooltip text)
41304 * @param {String} text The tab's text and tooltip
41306 setText : function(text){
41308 this.textEl.update(text);
41309 this.setTooltip(text);
41310 //if(!this.tabPanel.resizeTabs){
41311 // this.autoSize();
41315 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41317 activate : function(){
41318 this.tabPanel.activate(this.id);
41322 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41324 disable : function(){
41325 if(this.tabPanel.active != this){
41326 this.disabled = true;
41327 this.status_node.addClass("disabled");
41332 * Enables this TabPanelItem if it was previously disabled.
41334 enable : function(){
41335 this.disabled = false;
41336 this.status_node.removeClass("disabled");
41340 * Sets the content for this TabPanelItem.
41341 * @param {String} content The content
41342 * @param {Boolean} loadScripts true to look for and load scripts
41344 setContent : function(content, loadScripts){
41345 this.bodyEl.update(content, loadScripts);
41349 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41350 * @return {Roo.UpdateManager} The UpdateManager
41352 getUpdateManager : function(){
41353 return this.bodyEl.getUpdateManager();
41357 * Set a URL to be used to load the content for this TabPanelItem.
41358 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41359 * @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)
41360 * @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)
41361 * @return {Roo.UpdateManager} The UpdateManager
41363 setUrl : function(url, params, loadOnce){
41364 if(this.refreshDelegate){
41365 this.un('activate', this.refreshDelegate);
41367 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41368 this.on("activate", this.refreshDelegate);
41369 return this.bodyEl.getUpdateManager();
41373 _handleRefresh : function(url, params, loadOnce){
41374 if(!loadOnce || !this.loaded){
41375 var updater = this.bodyEl.getUpdateManager();
41376 updater.update(url, params, this._setLoaded.createDelegate(this));
41381 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41382 * Will fail silently if the setUrl method has not been called.
41383 * This does not activate the panel, just updates its content.
41385 refresh : function(){
41386 if(this.refreshDelegate){
41387 this.loaded = false;
41388 this.refreshDelegate();
41393 _setLoaded : function(){
41394 this.loaded = true;
41398 closeClick : function(e){
41401 this.fireEvent("beforeclose", this, o);
41402 if(o.cancel !== true){
41403 this.tabPanel.removeTab(this.id);
41407 * The text displayed in the tooltip for the close icon.
41410 closeText : "Close this tab"
41413 * This script refer to:
41414 * Title: International Telephone Input
41415 * Author: Jack O'Connor
41416 * Code version: v12.1.12
41417 * Availability: https://github.com/jackocnr/intl-tel-input.git
41420 Roo.bootstrap.PhoneInputData = function() {
41423 "Afghanistan (افغانستان)",
41428 "Albania (Shqipëri)",
41433 "Algeria (الجزائر)",
41458 "Antigua and Barbuda",
41468 "Armenia (Հայաստան)",
41484 "Austria (Österreich)",
41489 "Azerbaijan (Azərbaycan)",
41499 "Bahrain (البحرين)",
41504 "Bangladesh (বাংলাদেশ)",
41514 "Belarus (Беларусь)",
41519 "Belgium (België)",
41549 "Bosnia and Herzegovina (Босна и Херцеговина)",
41564 "British Indian Ocean Territory",
41569 "British Virgin Islands",
41579 "Bulgaria (България)",
41589 "Burundi (Uburundi)",
41594 "Cambodia (កម្ពុជា)",
41599 "Cameroon (Cameroun)",
41608 ["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"]
41611 "Cape Verde (Kabu Verdi)",
41616 "Caribbean Netherlands",
41627 "Central African Republic (République centrafricaine)",
41647 "Christmas Island",
41653 "Cocos (Keeling) Islands",
41664 "Comoros (جزر القمر)",
41669 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41674 "Congo (Republic) (Congo-Brazzaville)",
41694 "Croatia (Hrvatska)",
41715 "Czech Republic (Česká republika)",
41720 "Denmark (Danmark)",
41735 "Dominican Republic (República Dominicana)",
41739 ["809", "829", "849"]
41757 "Equatorial Guinea (Guinea Ecuatorial)",
41777 "Falkland Islands (Islas Malvinas)",
41782 "Faroe Islands (Føroyar)",
41803 "French Guiana (Guyane française)",
41808 "French Polynesia (Polynésie française)",
41823 "Georgia (საქართველო)",
41828 "Germany (Deutschland)",
41848 "Greenland (Kalaallit Nunaat)",
41885 "Guinea-Bissau (Guiné Bissau)",
41910 "Hungary (Magyarország)",
41915 "Iceland (Ísland)",
41935 "Iraq (العراق)",
41951 "Israel (ישראל)",
41978 "Jordan (الأردن)",
41983 "Kazakhstan (Казахстан)",
42004 "Kuwait (الكويت)",
42009 "Kyrgyzstan (Кыргызстан)",
42019 "Latvia (Latvija)",
42024 "Lebanon (لبنان)",
42039 "Libya (ليبيا)",
42049 "Lithuania (Lietuva)",
42064 "Macedonia (FYROM) (Македонија)",
42069 "Madagascar (Madagasikara)",
42099 "Marshall Islands",
42109 "Mauritania (موريتانيا)",
42114 "Mauritius (Moris)",
42135 "Moldova (Republica Moldova)",
42145 "Mongolia (Монгол)",
42150 "Montenegro (Crna Gora)",
42160 "Morocco (المغرب)",
42166 "Mozambique (Moçambique)",
42171 "Myanmar (Burma) (မြန်မာ)",
42176 "Namibia (Namibië)",
42191 "Netherlands (Nederland)",
42196 "New Caledonia (Nouvelle-Calédonie)",
42231 "North Korea (조선 민주주의 인민 공화국)",
42236 "Northern Mariana Islands",
42252 "Pakistan (پاکستان)",
42262 "Palestine (فلسطين)",
42272 "Papua New Guinea",
42314 "Réunion (La Réunion)",
42320 "Romania (România)",
42336 "Saint Barthélemy",
42347 "Saint Kitts and Nevis",
42357 "Saint Martin (Saint-Martin (partie française))",
42363 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42368 "Saint Vincent and the Grenadines",
42383 "São Tomé and Príncipe (São Tomé e Príncipe)",
42388 "Saudi Arabia (المملكة العربية السعودية)",
42393 "Senegal (Sénégal)",
42423 "Slovakia (Slovensko)",
42428 "Slovenia (Slovenija)",
42438 "Somalia (Soomaaliya)",
42448 "South Korea (대한민국)",
42453 "South Sudan (جنوب السودان)",
42463 "Sri Lanka (ශ්රී ලංකාව)",
42468 "Sudan (السودان)",
42478 "Svalbard and Jan Mayen",
42489 "Sweden (Sverige)",
42494 "Switzerland (Schweiz)",
42499 "Syria (سوريا)",
42544 "Trinidad and Tobago",
42549 "Tunisia (تونس)",
42554 "Turkey (Türkiye)",
42564 "Turks and Caicos Islands",
42574 "U.S. Virgin Islands",
42584 "Ukraine (Україна)",
42589 "United Arab Emirates (الإمارات العربية المتحدة)",
42611 "Uzbekistan (Oʻzbekiston)",
42621 "Vatican City (Città del Vaticano)",
42632 "Vietnam (Việt Nam)",
42637 "Wallis and Futuna (Wallis-et-Futuna)",
42642 "Western Sahara (الصحراء الغربية)",
42648 "Yemen (اليمن)",
42672 * This script refer to:
42673 * Title: International Telephone Input
42674 * Author: Jack O'Connor
42675 * Code version: v12.1.12
42676 * Availability: https://github.com/jackocnr/intl-tel-input.git
42680 * @class Roo.bootstrap.PhoneInput
42681 * @extends Roo.bootstrap.TriggerField
42682 * An input with International dial-code selection
42684 * @cfg {String} defaultDialCode default '+852'
42685 * @cfg {Array} preferedCountries default []
42688 * Create a new PhoneInput.
42689 * @param {Object} config Configuration options
42692 Roo.bootstrap.PhoneInput = function(config) {
42693 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42696 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42698 listWidth: undefined,
42700 selectedClass: 'active',
42702 invalidClass : "has-warning",
42704 validClass: 'has-success',
42706 allowed: '0123456789',
42711 * @cfg {String} defaultDialCode The default dial code when initializing the input
42713 defaultDialCode: '+852',
42716 * @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
42718 preferedCountries: false,
42720 getAutoCreate : function()
42722 var data = Roo.bootstrap.PhoneInputData();
42723 var align = this.labelAlign || this.parentLabelAlign();
42726 this.allCountries = [];
42727 this.dialCodeMapping = [];
42729 for (var i = 0; i < data.length; i++) {
42731 this.allCountries[i] = {
42735 priority: c[3] || 0,
42736 areaCodes: c[4] || null
42738 this.dialCodeMapping[c[2]] = {
42741 priority: c[3] || 0,
42742 areaCodes: c[4] || null
42754 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42755 maxlength: this.max_length,
42756 cls : 'form-control tel-input',
42757 autocomplete: 'new-password'
42760 var hiddenInput = {
42763 cls: 'hidden-tel-input'
42767 hiddenInput.name = this.name;
42770 if (this.disabled) {
42771 input.disabled = true;
42774 var flag_container = {
42791 cls: this.hasFeedback ? 'has-feedback' : '',
42797 cls: 'dial-code-holder',
42804 cls: 'roo-select2-container input-group',
42811 if (this.fieldLabel.length) {
42814 tooltip: 'This field is required'
42820 cls: 'control-label',
42826 html: this.fieldLabel
42829 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42835 if(this.indicatorpos == 'right') {
42836 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42843 if(align == 'left') {
42851 if(this.labelWidth > 12){
42852 label.style = "width: " + this.labelWidth + 'px';
42854 if(this.labelWidth < 13 && this.labelmd == 0){
42855 this.labelmd = this.labelWidth;
42857 if(this.labellg > 0){
42858 label.cls += ' col-lg-' + this.labellg;
42859 input.cls += ' col-lg-' + (12 - this.labellg);
42861 if(this.labelmd > 0){
42862 label.cls += ' col-md-' + this.labelmd;
42863 container.cls += ' col-md-' + (12 - this.labelmd);
42865 if(this.labelsm > 0){
42866 label.cls += ' col-sm-' + this.labelsm;
42867 container.cls += ' col-sm-' + (12 - this.labelsm);
42869 if(this.labelxs > 0){
42870 label.cls += ' col-xs-' + this.labelxs;
42871 container.cls += ' col-xs-' + (12 - this.labelxs);
42881 var settings = this;
42883 ['xs','sm','md','lg'].map(function(size){
42884 if (settings[size]) {
42885 cfg.cls += ' col-' + size + '-' + settings[size];
42889 this.store = new Roo.data.Store({
42890 proxy : new Roo.data.MemoryProxy({}),
42891 reader : new Roo.data.JsonReader({
42902 'name' : 'dialCode',
42906 'name' : 'priority',
42910 'name' : 'areaCodes',
42917 if(!this.preferedCountries) {
42918 this.preferedCountries = [
42925 var p = this.preferedCountries.reverse();
42928 for (var i = 0; i < p.length; i++) {
42929 for (var j = 0; j < this.allCountries.length; j++) {
42930 if(this.allCountries[j].iso2 == p[i]) {
42931 var t = this.allCountries[j];
42932 this.allCountries.splice(j,1);
42933 this.allCountries.unshift(t);
42939 this.store.proxy.data = {
42941 data: this.allCountries
42947 initEvents : function()
42950 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42952 this.indicator = this.indicatorEl();
42953 this.flag = this.flagEl();
42954 this.dialCodeHolder = this.dialCodeHolderEl();
42956 this.trigger = this.el.select('div.flag-box',true).first();
42957 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42962 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42963 _this.list.setWidth(lw);
42966 this.list.on('mouseover', this.onViewOver, this);
42967 this.list.on('mousemove', this.onViewMove, this);
42968 this.inputEl().on("keyup", this.onKeyUp, this);
42969 this.inputEl().on("keypress", this.onKeyPress, this);
42971 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42973 this.view = new Roo.View(this.list, this.tpl, {
42974 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42977 this.view.on('click', this.onViewClick, this);
42978 this.setValue(this.defaultDialCode);
42981 onTriggerClick : function(e)
42983 Roo.log('trigger click');
42988 if(this.isExpanded()){
42990 this.hasFocus = false;
42992 this.store.load({});
42993 this.hasFocus = true;
42998 isExpanded : function()
43000 return this.list.isVisible();
43003 collapse : function()
43005 if(!this.isExpanded()){
43009 Roo.get(document).un('mousedown', this.collapseIf, this);
43010 Roo.get(document).un('mousewheel', this.collapseIf, this);
43011 this.fireEvent('collapse', this);
43015 expand : function()
43019 if(this.isExpanded() || !this.hasFocus){
43023 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43024 this.list.setWidth(lw);
43027 this.restrictHeight();
43029 Roo.get(document).on('mousedown', this.collapseIf, this);
43030 Roo.get(document).on('mousewheel', this.collapseIf, this);
43032 this.fireEvent('expand', this);
43035 restrictHeight : function()
43037 this.list.alignTo(this.inputEl(), this.listAlign);
43038 this.list.alignTo(this.inputEl(), this.listAlign);
43041 onViewOver : function(e, t)
43043 if(this.inKeyMode){
43046 var item = this.view.findItemFromChild(t);
43049 var index = this.view.indexOf(item);
43050 this.select(index, false);
43055 onViewClick : function(view, doFocus, el, e)
43057 var index = this.view.getSelectedIndexes()[0];
43059 var r = this.store.getAt(index);
43062 this.onSelect(r, index);
43064 if(doFocus !== false && !this.blockFocus){
43065 this.inputEl().focus();
43069 onViewMove : function(e, t)
43071 this.inKeyMode = false;
43074 select : function(index, scrollIntoView)
43076 this.selectedIndex = index;
43077 this.view.select(index);
43078 if(scrollIntoView !== false){
43079 var el = this.view.getNode(index);
43081 this.list.scrollChildIntoView(el, false);
43086 createList : function()
43088 this.list = Roo.get(document.body).createChild({
43090 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43091 style: 'display:none'
43094 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43097 collapseIf : function(e)
43099 var in_combo = e.within(this.el);
43100 var in_list = e.within(this.list);
43101 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43103 if (in_combo || in_list || is_list) {
43109 onSelect : function(record, index)
43111 if(this.fireEvent('beforeselect', this, record, index) !== false){
43113 this.setFlagClass(record.data.iso2);
43114 this.setDialCode(record.data.dialCode);
43115 this.hasFocus = false;
43117 this.fireEvent('select', this, record, index);
43121 flagEl : function()
43123 var flag = this.el.select('div.flag',true).first();
43130 dialCodeHolderEl : function()
43132 var d = this.el.select('input.dial-code-holder',true).first();
43139 setDialCode : function(v)
43141 this.dialCodeHolder.dom.value = '+'+v;
43144 setFlagClass : function(n)
43146 this.flag.dom.className = 'flag '+n;
43149 getValue : function()
43151 var v = this.inputEl().getValue();
43152 if(this.dialCodeHolder) {
43153 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43158 setValue : function(v)
43160 var d = this.getDialCode(v);
43162 //invalid dial code
43163 if(v.length == 0 || !d || d.length == 0) {
43165 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43166 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43172 this.setFlagClass(this.dialCodeMapping[d].iso2);
43173 this.setDialCode(d);
43174 this.inputEl().dom.value = v.replace('+'+d,'');
43175 this.hiddenEl().dom.value = this.getValue();
43180 getDialCode : function(v)
43184 if (v.length == 0) {
43185 return this.dialCodeHolder.dom.value;
43189 if (v.charAt(0) != "+") {
43192 var numericChars = "";
43193 for (var i = 1; i < v.length; i++) {
43194 var c = v.charAt(i);
43197 if (this.dialCodeMapping[numericChars]) {
43198 dialCode = v.substr(1, i);
43200 if (numericChars.length == 4) {
43210 this.setValue(this.defaultDialCode);
43214 hiddenEl : function()
43216 return this.el.select('input.hidden-tel-input',true).first();
43219 // after setting val
43220 onKeyUp : function(e){
43221 this.setValue(this.getValue());
43224 onKeyPress : function(e){
43225 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43232 * @class Roo.bootstrap.MoneyField
43233 * @extends Roo.bootstrap.ComboBox
43234 * Bootstrap MoneyField class
43237 * Create a new MoneyField.
43238 * @param {Object} config Configuration options
43241 Roo.bootstrap.MoneyField = function(config) {
43243 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43247 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43250 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43252 allowDecimals : true,
43254 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43256 decimalSeparator : ".",
43258 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43260 decimalPrecision : 0,
43262 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43264 allowNegative : true,
43266 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43270 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43272 minValue : Number.NEGATIVE_INFINITY,
43274 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43276 maxValue : Number.MAX_VALUE,
43278 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43280 minText : "The minimum value for this field is {0}",
43282 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43284 maxText : "The maximum value for this field is {0}",
43286 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43287 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43289 nanText : "{0} is not a valid number",
43291 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43295 * @cfg {String} defaults currency of the MoneyField
43296 * value should be in lkey
43298 defaultCurrency : false,
43300 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43302 thousandsDelimiter : false,
43304 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43315 getAutoCreate : function()
43317 var align = this.labelAlign || this.parentLabelAlign();
43329 cls : 'form-control roo-money-amount-input',
43330 autocomplete: 'new-password'
43333 var hiddenInput = {
43337 cls: 'hidden-number-input'
43340 if(this.max_length) {
43341 input.maxlength = this.max_length;
43345 hiddenInput.name = this.name;
43348 if (this.disabled) {
43349 input.disabled = true;
43352 var clg = 12 - this.inputlg;
43353 var cmd = 12 - this.inputmd;
43354 var csm = 12 - this.inputsm;
43355 var cxs = 12 - this.inputxs;
43359 cls : 'row roo-money-field',
43363 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43367 cls: 'roo-select2-container input-group',
43371 cls : 'form-control roo-money-currency-input',
43372 autocomplete: 'new-password',
43374 name : this.currencyName
43378 cls : 'input-group-addon',
43392 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43396 cls: this.hasFeedback ? 'has-feedback' : '',
43407 if (this.fieldLabel.length) {
43410 tooltip: 'This field is required'
43416 cls: 'control-label',
43422 html: this.fieldLabel
43425 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43431 if(this.indicatorpos == 'right') {
43432 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43439 if(align == 'left') {
43447 if(this.labelWidth > 12){
43448 label.style = "width: " + this.labelWidth + 'px';
43450 if(this.labelWidth < 13 && this.labelmd == 0){
43451 this.labelmd = this.labelWidth;
43453 if(this.labellg > 0){
43454 label.cls += ' col-lg-' + this.labellg;
43455 input.cls += ' col-lg-' + (12 - this.labellg);
43457 if(this.labelmd > 0){
43458 label.cls += ' col-md-' + this.labelmd;
43459 container.cls += ' col-md-' + (12 - this.labelmd);
43461 if(this.labelsm > 0){
43462 label.cls += ' col-sm-' + this.labelsm;
43463 container.cls += ' col-sm-' + (12 - this.labelsm);
43465 if(this.labelxs > 0){
43466 label.cls += ' col-xs-' + this.labelxs;
43467 container.cls += ' col-xs-' + (12 - this.labelxs);
43478 var settings = this;
43480 ['xs','sm','md','lg'].map(function(size){
43481 if (settings[size]) {
43482 cfg.cls += ' col-' + size + '-' + settings[size];
43489 initEvents : function()
43491 this.indicator = this.indicatorEl();
43493 this.initCurrencyEvent();
43495 this.initNumberEvent();
43498 initCurrencyEvent : function()
43501 throw "can not find store for combo";
43504 this.store = Roo.factory(this.store, Roo.data);
43505 this.store.parent = this;
43509 this.triggerEl = this.el.select('.input-group-addon', true).first();
43511 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43516 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43517 _this.list.setWidth(lw);
43520 this.list.on('mouseover', this.onViewOver, this);
43521 this.list.on('mousemove', this.onViewMove, this);
43522 this.list.on('scroll', this.onViewScroll, this);
43525 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43528 this.view = new Roo.View(this.list, this.tpl, {
43529 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43532 this.view.on('click', this.onViewClick, this);
43534 this.store.on('beforeload', this.onBeforeLoad, this);
43535 this.store.on('load', this.onLoad, this);
43536 this.store.on('loadexception', this.onLoadException, this);
43538 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43539 "up" : function(e){
43540 this.inKeyMode = true;
43544 "down" : function(e){
43545 if(!this.isExpanded()){
43546 this.onTriggerClick();
43548 this.inKeyMode = true;
43553 "enter" : function(e){
43556 if(this.fireEvent("specialkey", this, e)){
43557 this.onViewClick(false);
43563 "esc" : function(e){
43567 "tab" : function(e){
43570 if(this.fireEvent("specialkey", this, e)){
43571 this.onViewClick(false);
43579 doRelay : function(foo, bar, hname){
43580 if(hname == 'down' || this.scope.isExpanded()){
43581 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43589 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43593 initNumberEvent : function(e)
43595 this.inputEl().on("keydown" , this.fireKey, this);
43596 this.inputEl().on("focus", this.onFocus, this);
43597 this.inputEl().on("blur", this.onBlur, this);
43599 this.inputEl().relayEvent('keyup', this);
43601 if(this.indicator){
43602 this.indicator.addClass('invisible');
43605 this.originalValue = this.getValue();
43607 if(this.validationEvent == 'keyup'){
43608 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43609 this.inputEl().on('keyup', this.filterValidation, this);
43611 else if(this.validationEvent !== false){
43612 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43615 if(this.selectOnFocus){
43616 this.on("focus", this.preFocus, this);
43619 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43620 this.inputEl().on("keypress", this.filterKeys, this);
43622 this.inputEl().relayEvent('keypress', this);
43625 var allowed = "0123456789";
43627 if(this.allowDecimals){
43628 allowed += this.decimalSeparator;
43631 if(this.allowNegative){
43635 if(this.thousandsDelimiter) {
43639 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43641 var keyPress = function(e){
43643 var k = e.getKey();
43645 var c = e.getCharCode();
43648 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43649 allowed.indexOf(String.fromCharCode(c)) === -1
43655 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43659 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43664 this.inputEl().on("keypress", keyPress, this);
43668 onTriggerClick : function(e)
43675 this.loadNext = false;
43677 if(this.isExpanded()){
43682 this.hasFocus = true;
43684 if(this.triggerAction == 'all') {
43685 this.doQuery(this.allQuery, true);
43689 this.doQuery(this.getRawValue());
43692 getCurrency : function()
43694 var v = this.currencyEl().getValue();
43699 restrictHeight : function()
43701 this.list.alignTo(this.currencyEl(), this.listAlign);
43702 this.list.alignTo(this.currencyEl(), this.listAlign);
43705 onViewClick : function(view, doFocus, el, e)
43707 var index = this.view.getSelectedIndexes()[0];
43709 var r = this.store.getAt(index);
43712 this.onSelect(r, index);
43716 onSelect : function(record, index){
43718 if(this.fireEvent('beforeselect', this, record, index) !== false){
43720 this.setFromCurrencyData(index > -1 ? record.data : false);
43724 this.fireEvent('select', this, record, index);
43728 setFromCurrencyData : function(o)
43732 this.lastCurrency = o;
43734 if (this.currencyField) {
43735 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43737 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43740 this.lastSelectionText = currency;
43742 //setting default currency
43743 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43744 this.setCurrency(this.defaultCurrency);
43748 this.setCurrency(currency);
43751 setFromData : function(o)
43755 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43757 this.setFromCurrencyData(c);
43762 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43764 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43767 this.setValue(value);
43771 setCurrency : function(v)
43773 this.currencyValue = v;
43776 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43781 setValue : function(v)
43783 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43789 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43791 this.inputEl().dom.value = (v == '') ? '' :
43792 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43794 if(!this.allowZero && v === '0') {
43795 this.hiddenEl().dom.value = '';
43796 this.inputEl().dom.value = '';
43803 getRawValue : function()
43805 var v = this.inputEl().getValue();
43810 getValue : function()
43812 return this.fixPrecision(this.parseValue(this.getRawValue()));
43815 parseValue : function(value)
43817 if(this.thousandsDelimiter) {
43819 r = new RegExp(",", "g");
43820 value = value.replace(r, "");
43823 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43824 return isNaN(value) ? '' : value;
43828 fixPrecision : function(value)
43830 if(this.thousandsDelimiter) {
43832 r = new RegExp(",", "g");
43833 value = value.replace(r, "");
43836 var nan = isNaN(value);
43838 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43839 return nan ? '' : value;
43841 return parseFloat(value).toFixed(this.decimalPrecision);
43844 decimalPrecisionFcn : function(v)
43846 return Math.floor(v);
43849 validateValue : function(value)
43851 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43855 var num = this.parseValue(value);
43858 this.markInvalid(String.format(this.nanText, value));
43862 if(num < this.minValue){
43863 this.markInvalid(String.format(this.minText, this.minValue));
43867 if(num > this.maxValue){
43868 this.markInvalid(String.format(this.maxText, this.maxValue));
43875 validate : function()
43877 if(this.disabled || this.allowBlank){
43882 var currency = this.getCurrency();
43884 if(this.validateValue(this.getRawValue()) && currency.length){
43889 this.markInvalid();
43893 getName: function()
43898 beforeBlur : function()
43904 var v = this.parseValue(this.getRawValue());
43911 onBlur : function()
43915 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43916 //this.el.removeClass(this.focusClass);
43919 this.hasFocus = false;
43921 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43925 var v = this.getValue();
43927 if(String(v) !== String(this.startValue)){
43928 this.fireEvent('change', this, v, this.startValue);
43931 this.fireEvent("blur", this);
43934 inputEl : function()
43936 return this.el.select('.roo-money-amount-input', true).first();
43939 currencyEl : function()
43941 return this.el.select('.roo-money-currency-input', true).first();
43944 hiddenEl : function()
43946 return this.el.select('input.hidden-number-input',true).first();
43950 * @class Roo.bootstrap.BezierSignature
43951 * @extends Roo.bootstrap.Component
43952 * Bootstrap BezierSignature class
43953 * This script refer to:
43954 * Title: Signature Pad
43956 * Availability: https://github.com/szimek/signature_pad
43959 * Create a new BezierSignature
43960 * @param {Object} config The config object
43963 Roo.bootstrap.BezierSignature = function(config){
43964 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43970 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43977 mouse_btn_down: true,
43980 * @cfg {int} canvas height
43982 canvas_height: '200px',
43985 * @cfg {float|function} Radius of a single dot.
43990 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43995 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44000 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44005 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44010 * @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.
44012 bg_color: 'rgba(0, 0, 0, 0)',
44015 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44017 dot_color: 'black',
44020 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44022 velocity_filter_weight: 0.7,
44025 * @cfg {function} Callback when stroke begin.
44030 * @cfg {function} Callback when stroke end.
44034 getAutoCreate : function()
44036 var cls = 'roo-signature column';
44039 cls += ' ' + this.cls;
44049 for(var i = 0; i < col_sizes.length; i++) {
44050 if(this[col_sizes[i]]) {
44051 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44061 cls: 'roo-signature-body',
44065 cls: 'roo-signature-body-canvas',
44066 height: this.canvas_height,
44067 width: this.canvas_width
44074 style: 'display: none'
44082 initEvents: function()
44084 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44086 var canvas = this.canvasEl();
44088 // mouse && touch event swapping...
44089 canvas.dom.style.touchAction = 'none';
44090 canvas.dom.style.msTouchAction = 'none';
44092 this.mouse_btn_down = false;
44093 canvas.on('mousedown', this._handleMouseDown, this);
44094 canvas.on('mousemove', this._handleMouseMove, this);
44095 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44097 if (window.PointerEvent) {
44098 canvas.on('pointerdown', this._handleMouseDown, this);
44099 canvas.on('pointermove', this._handleMouseMove, this);
44100 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44103 if ('ontouchstart' in window) {
44104 canvas.on('touchstart', this._handleTouchStart, this);
44105 canvas.on('touchmove', this._handleTouchMove, this);
44106 canvas.on('touchend', this._handleTouchEnd, this);
44109 Roo.EventManager.onWindowResize(this.resize, this, true);
44111 // file input event
44112 this.fileEl().on('change', this.uploadImage, this);
44119 resize: function(){
44121 var canvas = this.canvasEl().dom;
44122 var ctx = this.canvasElCtx();
44123 var img_data = false;
44125 if(canvas.width > 0) {
44126 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44128 // setting canvas width will clean img data
44131 var style = window.getComputedStyle ?
44132 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44134 var padding_left = parseInt(style.paddingLeft) || 0;
44135 var padding_right = parseInt(style.paddingRight) || 0;
44137 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44140 ctx.putImageData(img_data, 0, 0);
44144 _handleMouseDown: function(e)
44146 if (e.browserEvent.which === 1) {
44147 this.mouse_btn_down = true;
44148 this.strokeBegin(e);
44152 _handleMouseMove: function (e)
44154 if (this.mouse_btn_down) {
44155 this.strokeMoveUpdate(e);
44159 _handleMouseUp: function (e)
44161 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44162 this.mouse_btn_down = false;
44167 _handleTouchStart: function (e) {
44169 e.preventDefault();
44170 if (e.browserEvent.targetTouches.length === 1) {
44171 // var touch = e.browserEvent.changedTouches[0];
44172 // this.strokeBegin(touch);
44174 this.strokeBegin(e); // assume e catching the correct xy...
44178 _handleTouchMove: function (e) {
44179 e.preventDefault();
44180 // var touch = event.targetTouches[0];
44181 // _this._strokeMoveUpdate(touch);
44182 this.strokeMoveUpdate(e);
44185 _handleTouchEnd: function (e) {
44186 var wasCanvasTouched = e.target === this.canvasEl().dom;
44187 if (wasCanvasTouched) {
44188 e.preventDefault();
44189 // var touch = event.changedTouches[0];
44190 // _this._strokeEnd(touch);
44195 reset: function () {
44196 this._lastPoints = [];
44197 this._lastVelocity = 0;
44198 this._lastWidth = (this.min_width + this.max_width) / 2;
44199 this.canvasElCtx().fillStyle = this.dot_color;
44202 strokeMoveUpdate: function(e)
44204 this.strokeUpdate(e);
44206 if (this.throttle) {
44207 this.throttleStroke(this.strokeUpdate, this.throttle);
44210 this.strokeUpdate(e);
44214 strokeBegin: function(e)
44216 var newPointGroup = {
44217 color: this.dot_color,
44221 if (typeof this.onBegin === 'function') {
44225 this.curve_data.push(newPointGroup);
44227 this.strokeUpdate(e);
44230 strokeUpdate: function(e)
44232 var rect = this.canvasEl().dom.getBoundingClientRect();
44233 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44234 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44235 var lastPoints = lastPointGroup.points;
44236 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44237 var isLastPointTooClose = lastPoint
44238 ? point.distanceTo(lastPoint) <= this.min_distance
44240 var color = lastPointGroup.color;
44241 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44242 var curve = this.addPoint(point);
44244 this.drawDot({color: color, point: point});
44247 this.drawCurve({color: color, curve: curve});
44257 strokeEnd: function(e)
44259 this.strokeUpdate(e);
44260 if (typeof this.onEnd === 'function') {
44265 addPoint: function (point) {
44266 var _lastPoints = this._lastPoints;
44267 _lastPoints.push(point);
44268 if (_lastPoints.length > 2) {
44269 if (_lastPoints.length === 3) {
44270 _lastPoints.unshift(_lastPoints[0]);
44272 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44273 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44274 _lastPoints.shift();
44280 calculateCurveWidths: function (startPoint, endPoint) {
44281 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44282 (1 - this.velocity_filter_weight) * this._lastVelocity;
44284 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44287 start: this._lastWidth
44290 this._lastVelocity = velocity;
44291 this._lastWidth = newWidth;
44295 drawDot: function (_a) {
44296 var color = _a.color, point = _a.point;
44297 var ctx = this.canvasElCtx();
44298 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44300 this.drawCurveSegment(point.x, point.y, width);
44302 ctx.fillStyle = color;
44306 drawCurve: function (_a) {
44307 var color = _a.color, curve = _a.curve;
44308 var ctx = this.canvasElCtx();
44309 var widthDelta = curve.endWidth - curve.startWidth;
44310 var drawSteps = Math.floor(curve.length()) * 2;
44312 ctx.fillStyle = color;
44313 for (var i = 0; i < drawSteps; i += 1) {
44314 var t = i / drawSteps;
44320 var x = uuu * curve.startPoint.x;
44321 x += 3 * uu * t * curve.control1.x;
44322 x += 3 * u * tt * curve.control2.x;
44323 x += ttt * curve.endPoint.x;
44324 var y = uuu * curve.startPoint.y;
44325 y += 3 * uu * t * curve.control1.y;
44326 y += 3 * u * tt * curve.control2.y;
44327 y += ttt * curve.endPoint.y;
44328 var width = curve.startWidth + ttt * widthDelta;
44329 this.drawCurveSegment(x, y, width);
44335 drawCurveSegment: function (x, y, width) {
44336 var ctx = this.canvasElCtx();
44338 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44339 this.is_empty = false;
44344 var ctx = this.canvasElCtx();
44345 var canvas = this.canvasEl().dom;
44346 ctx.fillStyle = this.bg_color;
44347 ctx.clearRect(0, 0, canvas.width, canvas.height);
44348 ctx.fillRect(0, 0, canvas.width, canvas.height);
44349 this.curve_data = [];
44351 this.is_empty = true;
44356 return this.el.select('input',true).first();
44359 canvasEl: function()
44361 return this.el.select('canvas',true).first();
44364 canvasElCtx: function()
44366 return this.el.select('canvas',true).first().dom.getContext('2d');
44369 getImage: function(type)
44371 if(this.is_empty) {
44376 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44379 drawFromImage: function(img_src)
44381 var img = new Image();
44383 img.onload = function(){
44384 this.canvasElCtx().drawImage(img, 0, 0);
44389 this.is_empty = false;
44392 selectImage: function()
44394 this.fileEl().dom.click();
44397 uploadImage: function(e)
44399 var reader = new FileReader();
44401 reader.onload = function(e){
44402 var img = new Image();
44403 img.onload = function(){
44405 this.canvasElCtx().drawImage(img, 0, 0);
44407 img.src = e.target.result;
44410 reader.readAsDataURL(e.target.files[0]);
44413 // Bezier Point Constructor
44414 Point: (function () {
44415 function Point(x, y, time) {
44418 this.time = time || Date.now();
44420 Point.prototype.distanceTo = function (start) {
44421 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44423 Point.prototype.equals = function (other) {
44424 return this.x === other.x && this.y === other.y && this.time === other.time;
44426 Point.prototype.velocityFrom = function (start) {
44427 return this.time !== start.time
44428 ? this.distanceTo(start) / (this.time - start.time)
44435 // Bezier Constructor
44436 Bezier: (function () {
44437 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44438 this.startPoint = startPoint;
44439 this.control2 = control2;
44440 this.control1 = control1;
44441 this.endPoint = endPoint;
44442 this.startWidth = startWidth;
44443 this.endWidth = endWidth;
44445 Bezier.fromPoints = function (points, widths, scope) {
44446 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44447 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44448 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44450 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44451 var dx1 = s1.x - s2.x;
44452 var dy1 = s1.y - s2.y;
44453 var dx2 = s2.x - s3.x;
44454 var dy2 = s2.y - s3.y;
44455 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44456 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44457 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44458 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44459 var dxm = m1.x - m2.x;
44460 var dym = m1.y - m2.y;
44461 var k = l2 / (l1 + l2);
44462 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44463 var tx = s2.x - cm.x;
44464 var ty = s2.y - cm.y;
44466 c1: new scope.Point(m1.x + tx, m1.y + ty),
44467 c2: new scope.Point(m2.x + tx, m2.y + ty)
44470 Bezier.prototype.length = function () {
44475 for (var i = 0; i <= steps; i += 1) {
44477 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44478 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44480 var xdiff = cx - px;
44481 var ydiff = cy - py;
44482 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44489 Bezier.prototype.point = function (t, start, c1, c2, end) {
44490 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44491 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44492 + (3.0 * c2 * (1.0 - t) * t * t)
44493 + (end * t * t * t);
44498 throttleStroke: function(fn, wait) {
44499 if (wait === void 0) { wait = 250; }
44501 var timeout = null;
44505 var later = function () {
44506 previous = Date.now();
44508 result = fn.apply(storedContext, storedArgs);
44510 storedContext = null;
44514 return function wrapper() {
44516 for (var _i = 0; _i < arguments.length; _i++) {
44517 args[_i] = arguments[_i];
44519 var now = Date.now();
44520 var remaining = wait - (now - previous);
44521 storedContext = this;
44523 if (remaining <= 0 || remaining > wait) {
44525 clearTimeout(timeout);
44529 result = fn.apply(storedContext, storedArgs);
44531 storedContext = null;
44535 else if (!timeout) {
44536 timeout = window.setTimeout(later, remaining);