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, //
2078 header_imageEl : false,
2080 layoutCls : function()
2084 Roo.log(this.margin_bottom.length);
2085 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2086 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2088 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2089 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2091 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2092 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2096 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2097 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2098 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2102 // more generic support?
2110 // Roo.log("Call onRender: " + this.xtype);
2111 /* We are looking at something like this.
2113 <img src="..." class="card-img-top" alt="...">
2114 <div class="card-body">
2115 <h5 class="card-title">Card title</h5>
2116 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2118 >> this bit is really the body...
2119 <div> << we will ad dthis in hopefully it will not break shit.
2121 ** card text does not actually have any styling...
2123 <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>
2126 <a href="#" class="card-link">Card link</a>
2129 <div class="card-footer">
2130 <small class="text-muted">Last updated 3 mins ago</small>
2134 getAutoCreate : function(){
2142 if (this.weight.length && this.weight != 'light') {
2143 cfg.cls += ' text-white';
2145 cfg.cls += ' text-dark'; // need as it's nested..
2147 if (this.weight.length) {
2148 cfg.cls += ' bg-' + this.weight;
2151 cfg.cls += ' ' + this.layoutCls();
2154 var hdr_ctr = false;
2155 if (this.header.length) {
2157 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2158 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2172 if (this.collapsable) {
2175 cls : 'd-block user-select-none',
2179 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2184 hdr.cn.push(hdr_ctr);
2189 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2194 if (this.header_image.length) {
2197 cls : 'card-img-top',
2198 src: this.header_image // escape?
2203 cls : 'card-img-top d-none'
2209 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2213 if (this.collapsable || this.rotateable) {
2216 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2223 if (this.title.length) {
2227 src: this.title // escape?
2231 if (this.subtitle.length) {
2235 src: this.subtitle // escape?
2241 cls : 'roo-card-body-ctr'
2244 if (this.html.length) {
2250 // fixme ? handle objects?
2252 if (this.footer.length) {
2255 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2260 cfg.cn.push({cls : 'card-footer d-none'});
2269 getCardHeader : function()
2271 var ret = this.el.select('.card-header',true).first();
2272 if (ret.hasClass('d-none')) {
2273 ret.removeClass('d-none');
2278 getCardFooter : function()
2280 var ret = this.el.select('.card-footer',true).first();
2281 if (ret.hasClass('d-none')) {
2282 ret.removeClass('d-none');
2287 getCardImageTop : function()
2289 var ret = this.header_imageEl;
2290 if (ret.hasClass('d-none')) {
2291 ret.removeClass('d-none');
2297 getChildContainer : function()
2303 return this.el.select('.roo-card-body-ctr',true).first();
2306 initEvents: function()
2308 this.bodyEl = this.el.select('.card-body',true).first();
2309 this.containerEl = this.getChildContainer();
2311 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2312 containerScroll: true,
2313 ddGroup: this.drag_group || 'default_card_drag_group'
2315 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2317 if (this.dropable) {
2318 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2319 containerScroll: true,
2320 ddGroup: this.drop_group || 'default_card_drag_group'
2322 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2323 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2324 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2325 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2326 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2329 if (this.collapsable) {
2330 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2332 if (this.rotateable) {
2333 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2335 this.collapsableEl = this.el.select('.roo-collapsable').first();
2337 this.footerEl = this.el.select('.card-footer').first();
2338 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2339 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2340 this.headerEl = this.el.select('.card-header',true).first();
2343 this.el.addClass('roo-card-rotated');
2344 this.fireEvent('rotate', this, true);
2346 this.header_imageEl = this.el.select('.card-img-top',true).first();
2347 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2350 getDragData : function(e)
2352 var target = this.getEl();
2354 //this.handleSelection(e);
2359 nodes: this.getEl(),
2364 dragData.ddel = target.dom ; // the div element
2365 Roo.log(target.getWidth( ));
2366 dragData.ddel.style.width = target.getWidth() + 'px';
2373 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2374 * whole Element becomes the target, and this causes the drop gesture to append.
2376 getTargetFromEvent : function(e, dragged_card_el)
2378 var target = e.getTarget();
2379 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2380 target = target.parentNode;
2391 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2392 // see if target is one of the 'cards'...
2395 //Roo.log(this.items.length);
2398 var last_card_n = 0;
2400 for (var i = 0;i< this.items.length;i++) {
2402 if (!this.items[i].el.hasClass('card')) {
2405 pos = this.getDropPoint(e, this.items[i].el.dom);
2407 cards_len = ret.cards.length;
2408 //Roo.log(this.items[i].el.dom.id);
2409 ret.cards.push(this.items[i]);
2411 if (ret.card_n < 0 && pos == 'above') {
2412 ret.position = cards_len > 0 ? 'below' : pos;
2413 ret.items_n = i > 0 ? i - 1 : 0;
2414 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2415 ret.card = ret.cards[ret.card_n];
2418 if (!ret.cards.length) {
2420 ret.position = 'below';
2424 // could not find a card.. stick it at the end..
2425 if (ret.card_n < 0) {
2426 ret.card_n = last_card_n;
2427 ret.card = ret.cards[last_card_n];
2428 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2429 ret.position = 'below';
2432 if (this.items[ret.items_n].el == dragged_card_el) {
2436 if (ret.position == 'below') {
2437 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2439 if (card_after && card_after.el == dragged_card_el) {
2446 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2448 if (card_before && card_before.el == dragged_card_el) {
2455 onNodeEnter : function(n, dd, e, data){
2458 onNodeOver : function(n, dd, e, data)
2461 var target_info = this.getTargetFromEvent(e,data.source.el);
2462 if (target_info === false) {
2463 this.dropPlaceHolder('hide');
2466 Roo.log(['getTargetFromEvent', target_info ]);
2469 this.dropPlaceHolder('show', target_info,data);
2473 onNodeOut : function(n, dd, e, data){
2474 this.dropPlaceHolder('hide');
2477 onNodeDrop : function(n, dd, e, data)
2480 // call drop - return false if
2482 // this could actually fail - if the Network drops..
2483 // we will ignore this at present..- client should probably reload
2484 // the whole set of cards if stuff like that fails.
2487 var info = this.getTargetFromEvent(e,data.source.el);
2488 if (info === false) {
2491 this.dropPlaceHolder('hide');
2497 this.acceptCard(data.source, info.position, info.card, info.items_n);
2501 firstChildCard : function()
2503 for (var i = 0;i< this.items.length;i++) {
2505 if (!this.items[i].el.hasClass('card')) {
2508 return this.items[i];
2510 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2515 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2517 acceptCard : function(move_card, position, next_to_card )
2519 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2523 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2525 move_card.parent().removeCard(move_card);
2528 var dom = move_card.el.dom;
2529 dom.style.width = ''; // clear with - which is set by drag.
2531 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2532 var cardel = next_to_card.el.dom;
2534 if (position == 'above' ) {
2535 cardel.parentNode.insertBefore(dom, cardel);
2536 } else if (cardel.nextSibling) {
2537 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2539 cardel.parentNode.append(dom);
2542 // card container???
2543 this.containerEl.dom.append(dom);
2546 //FIXME HANDLE card = true
2548 // add this to the correct place in items.
2550 // remove Card from items.
2553 if (this.items.length) {
2555 //Roo.log([info.items_n, info.position, this.items.length]);
2556 for (var i =0; i < this.items.length; i++) {
2557 if (i == to_items_n && position == 'above') {
2558 nitems.push(move_card);
2560 nitems.push(this.items[i]);
2561 if (i == to_items_n && position == 'below') {
2562 nitems.push(move_card);
2565 this.items = nitems;
2566 Roo.log(this.items);
2568 this.items.push(move_card);
2571 move_card.parentId = this.id;
2577 removeCard : function(c)
2579 this.items = this.items.filter(function(e) { return e != c });
2582 dom.parentNode.removeChild(dom);
2583 dom.style.width = ''; // clear with - which is set by drag.
2588 /** Decide whether to drop above or below a View node. */
2589 getDropPoint : function(e, n, dd)
2594 if (n == this.containerEl.dom) {
2597 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2598 var c = t + (b - t) / 2;
2599 var y = Roo.lib.Event.getPageY(e);
2606 onToggleCollapse : function(e)
2608 if (this.collapsed) {
2609 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2610 this.collapsableEl.addClass('show');
2611 this.collapsed = false;
2614 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2615 this.collapsableEl.removeClass('show');
2616 this.collapsed = true;
2621 onToggleRotate : function(e)
2623 this.collapsableEl.removeClass('show');
2624 this.footerEl.removeClass('d-none');
2625 this.el.removeClass('roo-card-rotated');
2626 this.el.removeClass('d-none');
2629 this.collapsableEl.addClass('show');
2630 this.rotated = false;
2631 this.fireEvent('rotate', this, this.rotated);
2634 this.el.addClass('roo-card-rotated');
2635 this.footerEl.addClass('d-none');
2636 this.el.select('.roo-collapsable').removeClass('show');
2638 this.rotated = true;
2639 this.fireEvent('rotate', this, this.rotated);
2643 dropPlaceHolder: function (action, info, data)
2645 if (this.dropEl === false) {
2646 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2650 this.dropEl.removeClass(['d-none', 'd-block']);
2651 if (action == 'hide') {
2653 this.dropEl.addClass('d-none');
2656 // FIXME - info.card == true!!!
2657 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2659 if (info.card !== true) {
2660 var cardel = info.card.el.dom;
2662 if (info.position == 'above') {
2663 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2664 } else if (cardel.nextSibling) {
2665 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2667 cardel.parentNode.append(this.dropEl.dom);
2670 // card container???
2671 this.containerEl.dom.append(this.dropEl.dom);
2674 this.dropEl.addClass('d-block roo-card-dropzone');
2676 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2683 setHeaderText: function(html)
2686 if (this.headerContainerEl) {
2687 this.headerContainerEl.dom.innerHTML = html;
2690 onHeaderImageLoad : function(ev, he)
2692 if (!this.header_image_fit_square) {
2696 var hw = he.naturalHeight / he.naturalWidth;
2699 //var w = he.dom.naturalWidth;
2701 Roo.get(he).setX( 0 );
2703 var nw = (ww * (1/hw));
2704 Roo.get(he).setSize( ww * (1/hw), ww);
2705 Roo.get(he).setX( (ww - nw)/ 2);
2716 * Card header - holder for the card header elements.
2721 * @class Roo.bootstrap.CardHeader
2722 * @extends Roo.bootstrap.Element
2723 * Bootstrap CardHeader class
2725 * Create a new Card Header - that you can embed children into
2726 * @param {Object} config The config object
2729 Roo.bootstrap.CardHeader = function(config){
2730 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2733 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2736 container_method : 'getCardHeader'
2749 * Card footer - holder for the card footer elements.
2754 * @class Roo.bootstrap.CardFooter
2755 * @extends Roo.bootstrap.Element
2756 * Bootstrap CardFooter class
2758 * Create a new Card Footer - that you can embed children into
2759 * @param {Object} config The config object
2762 Roo.bootstrap.CardFooter = function(config){
2763 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2766 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2769 container_method : 'getCardFooter'
2782 * Card header - holder for the card header elements.
2787 * @class Roo.bootstrap.CardImageTop
2788 * @extends Roo.bootstrap.Element
2789 * Bootstrap CardImageTop class
2791 * Create a new Card Image Top container
2792 * @param {Object} config The config object
2795 Roo.bootstrap.CardImageTop = function(config){
2796 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2799 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2802 container_method : 'getCardImageTop'
2820 * @class Roo.bootstrap.Img
2821 * @extends Roo.bootstrap.Component
2822 * Bootstrap Img class
2823 * @cfg {Boolean} imgResponsive false | true
2824 * @cfg {String} border rounded | circle | thumbnail
2825 * @cfg {String} src image source
2826 * @cfg {String} alt image alternative text
2827 * @cfg {String} href a tag href
2828 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2829 * @cfg {String} xsUrl xs image source
2830 * @cfg {String} smUrl sm image source
2831 * @cfg {String} mdUrl md image source
2832 * @cfg {String} lgUrl lg image source
2835 * Create a new Input
2836 * @param {Object} config The config object
2839 Roo.bootstrap.Img = function(config){
2840 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2846 * The img click event for the img.
2847 * @param {Roo.EventObject} e
2853 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2855 imgResponsive: true,
2865 getAutoCreate : function()
2867 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2868 return this.createSingleImg();
2873 cls: 'roo-image-responsive-group',
2878 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2880 if(!_this[size + 'Url']){
2886 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2887 html: _this.html || cfg.html,
2888 src: _this[size + 'Url']
2891 img.cls += ' roo-image-responsive-' + size;
2893 var s = ['xs', 'sm', 'md', 'lg'];
2895 s.splice(s.indexOf(size), 1);
2897 Roo.each(s, function(ss){
2898 img.cls += ' hidden-' + ss;
2901 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2902 cfg.cls += ' img-' + _this.border;
2906 cfg.alt = _this.alt;
2919 a.target = _this.target;
2923 cfg.cn.push((_this.href) ? a : img);
2930 createSingleImg : function()
2934 cls: (this.imgResponsive) ? 'img-responsive' : '',
2936 src : 'about:blank' // just incase src get's set to undefined?!?
2939 cfg.html = this.html || cfg.html;
2941 cfg.src = this.src || cfg.src;
2943 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2944 cfg.cls += ' img-' + this.border;
2961 a.target = this.target;
2966 return (this.href) ? a : cfg;
2969 initEvents: function()
2972 this.el.on('click', this.onClick, this);
2977 onClick : function(e)
2979 Roo.log('img onclick');
2980 this.fireEvent('click', this, e);
2983 * Sets the url of the image - used to update it
2984 * @param {String} url the url of the image
2987 setSrc : function(url)
2991 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2992 this.el.dom.src = url;
2996 this.el.select('img', true).first().dom.src = url;
3012 * @class Roo.bootstrap.Link
3013 * @extends Roo.bootstrap.Component
3014 * Bootstrap Link Class
3015 * @cfg {String} alt image alternative text
3016 * @cfg {String} href a tag href
3017 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3018 * @cfg {String} html the content of the link.
3019 * @cfg {String} anchor name for the anchor link
3020 * @cfg {String} fa - favicon
3022 * @cfg {Boolean} preventDefault (true | false) default false
3026 * Create a new Input
3027 * @param {Object} config The config object
3030 Roo.bootstrap.Link = function(config){
3031 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3037 * The img click event for the img.
3038 * @param {Roo.EventObject} e
3044 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3048 preventDefault: false,
3054 getAutoCreate : function()
3056 var html = this.html || '';
3058 if (this.fa !== false) {
3059 html = '<i class="fa fa-' + this.fa + '"></i>';
3064 // anchor's do not require html/href...
3065 if (this.anchor === false) {
3067 cfg.href = this.href || '#';
3069 cfg.name = this.anchor;
3070 if (this.html !== false || this.fa !== false) {
3073 if (this.href !== false) {
3074 cfg.href = this.href;
3078 if(this.alt !== false){
3083 if(this.target !== false) {
3084 cfg.target = this.target;
3090 initEvents: function() {
3092 if(!this.href || this.preventDefault){
3093 this.el.on('click', this.onClick, this);
3097 onClick : function(e)
3099 if(this.preventDefault){
3102 //Roo.log('img onclick');
3103 this.fireEvent('click', this, e);
3116 * @class Roo.bootstrap.Header
3117 * @extends Roo.bootstrap.Component
3118 * Bootstrap Header class
3119 * @cfg {String} html content of header
3120 * @cfg {Number} level (1|2|3|4|5|6) default 1
3123 * Create a new Header
3124 * @param {Object} config The config object
3128 Roo.bootstrap.Header = function(config){
3129 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3132 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3140 getAutoCreate : function(){
3145 tag: 'h' + (1 *this.level),
3146 html: this.html || ''
3158 * Ext JS Library 1.1.1
3159 * Copyright(c) 2006-2007, Ext JS, LLC.
3161 * Originally Released Under LGPL - original licence link has changed is not relivant.
3164 * <script type="text/javascript">
3168 * @class Roo.bootstrap.MenuMgr
3169 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3172 Roo.bootstrap.MenuMgr = function(){
3173 var menus, active, groups = {}, attached = false, lastShow = new Date();
3175 // private - called when first menu is created
3178 active = new Roo.util.MixedCollection();
3179 Roo.get(document).addKeyListener(27, function(){
3180 if(active.length > 0){
3188 if(active && active.length > 0){
3189 var c = active.clone();
3199 if(active.length < 1){
3200 Roo.get(document).un("mouseup", onMouseDown);
3208 var last = active.last();
3209 lastShow = new Date();
3212 Roo.get(document).on("mouseup", onMouseDown);
3217 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3218 m.parentMenu.activeChild = m;
3219 }else if(last && last.isVisible()){
3220 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3225 function onBeforeHide(m){
3227 m.activeChild.hide();
3229 if(m.autoHideTimer){
3230 clearTimeout(m.autoHideTimer);
3231 delete m.autoHideTimer;
3236 function onBeforeShow(m){
3237 var pm = m.parentMenu;
3238 if(!pm && !m.allowOtherMenus){
3240 }else if(pm && pm.activeChild && active != m){
3241 pm.activeChild.hide();
3245 // private this should really trigger on mouseup..
3246 function onMouseDown(e){
3247 Roo.log("on Mouse Up");
3249 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3250 Roo.log("MenuManager hideAll");
3259 function onBeforeCheck(mi, state){
3261 var g = groups[mi.group];
3262 for(var i = 0, l = g.length; i < l; i++){
3264 g[i].setChecked(false);
3273 * Hides all menus that are currently visible
3275 hideAll : function(){
3280 register : function(menu){
3284 menus[menu.id] = menu;
3285 menu.on("beforehide", onBeforeHide);
3286 menu.on("hide", onHide);
3287 menu.on("beforeshow", onBeforeShow);
3288 menu.on("show", onShow);
3290 if(g && menu.events["checkchange"]){
3294 groups[g].push(menu);
3295 menu.on("checkchange", onCheck);
3300 * Returns a {@link Roo.menu.Menu} object
3301 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3302 * be used to generate and return a new Menu instance.
3304 get : function(menu){
3305 if(typeof menu == "string"){ // menu id
3307 }else if(menu.events){ // menu instance
3310 /*else if(typeof menu.length == 'number'){ // array of menu items?
3311 return new Roo.bootstrap.Menu({items:menu});
3312 }else{ // otherwise, must be a config
3313 return new Roo.bootstrap.Menu(menu);
3320 unregister : function(menu){
3321 delete menus[menu.id];
3322 menu.un("beforehide", onBeforeHide);
3323 menu.un("hide", onHide);
3324 menu.un("beforeshow", onBeforeShow);
3325 menu.un("show", onShow);
3327 if(g && menu.events["checkchange"]){
3328 groups[g].remove(menu);
3329 menu.un("checkchange", onCheck);
3334 registerCheckable : function(menuItem){
3335 var g = menuItem.group;
3340 groups[g].push(menuItem);
3341 menuItem.on("beforecheckchange", onBeforeCheck);
3346 unregisterCheckable : function(menuItem){
3347 var g = menuItem.group;
3349 groups[g].remove(menuItem);
3350 menuItem.un("beforecheckchange", onBeforeCheck);
3362 * @class Roo.bootstrap.Menu
3363 * @extends Roo.bootstrap.Component
3364 * Bootstrap Menu class - container for MenuItems
3365 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3366 * @cfg {bool} hidden if the menu should be hidden when rendered.
3367 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3368 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3372 * @param {Object} config The config object
3376 Roo.bootstrap.Menu = function(config){
3377 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3378 if (this.registerMenu && this.type != 'treeview') {
3379 Roo.bootstrap.MenuMgr.register(this);
3386 * Fires before this menu is displayed (return false to block)
3387 * @param {Roo.menu.Menu} this
3392 * Fires before this menu is hidden (return false to block)
3393 * @param {Roo.menu.Menu} this
3398 * Fires after this menu is displayed
3399 * @param {Roo.menu.Menu} this
3404 * Fires after this menu is hidden
3405 * @param {Roo.menu.Menu} this
3410 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3411 * @param {Roo.menu.Menu} this
3412 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3413 * @param {Roo.EventObject} e
3418 * Fires when the mouse is hovering over this menu
3419 * @param {Roo.menu.Menu} this
3420 * @param {Roo.EventObject} e
3421 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3426 * Fires when the mouse exits this menu
3427 * @param {Roo.menu.Menu} this
3428 * @param {Roo.EventObject} e
3429 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3434 * Fires when a menu item contained in this menu is clicked
3435 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3436 * @param {Roo.EventObject} e
3440 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3443 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3447 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3450 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3452 registerMenu : true,
3454 menuItems :false, // stores the menu items..
3464 getChildContainer : function() {
3468 getAutoCreate : function(){
3470 //if (['right'].indexOf(this.align)!==-1) {
3471 // cfg.cn[1].cls += ' pull-right'
3477 cls : 'dropdown-menu' ,
3478 style : 'z-index:1000'
3482 if (this.type === 'submenu') {
3483 cfg.cls = 'submenu active';
3485 if (this.type === 'treeview') {
3486 cfg.cls = 'treeview-menu';
3491 initEvents : function() {
3493 // Roo.log("ADD event");
3494 // Roo.log(this.triggerEl.dom);
3496 this.triggerEl.on('click', this.onTriggerClick, this);
3498 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3501 if (this.triggerEl.hasClass('nav-item')) {
3502 // dropdown toggle on the 'a' in BS4?
3503 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3505 this.triggerEl.addClass('dropdown-toggle');
3508 this.el.on('touchstart' , this.onTouch, this);
3510 this.el.on('click' , this.onClick, this);
3512 this.el.on("mouseover", this.onMouseOver, this);
3513 this.el.on("mouseout", this.onMouseOut, this);
3517 findTargetItem : function(e)
3519 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3523 //Roo.log(t); Roo.log(t.id);
3525 //Roo.log(this.menuitems);
3526 return this.menuitems.get(t.id);
3528 //return this.items.get(t.menuItemId);
3534 onTouch : function(e)
3536 Roo.log("menu.onTouch");
3537 //e.stopEvent(); this make the user popdown broken
3541 onClick : function(e)
3543 Roo.log("menu.onClick");
3545 var t = this.findTargetItem(e);
3546 if(!t || t.isContainer){
3551 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3552 if(t == this.activeItem && t.shouldDeactivate(e)){
3553 this.activeItem.deactivate();
3554 delete this.activeItem;
3558 this.setActiveItem(t, true);
3566 Roo.log('pass click event');
3570 this.fireEvent("click", this, t, e);
3574 if(!t.href.length || t.href == '#'){
3575 (function() { _this.hide(); }).defer(100);
3580 onMouseOver : function(e){
3581 var t = this.findTargetItem(e);
3584 // if(t.canActivate && !t.disabled){
3585 // this.setActiveItem(t, true);
3589 this.fireEvent("mouseover", this, e, t);
3591 isVisible : function(){
3592 return !this.hidden;
3594 onMouseOut : function(e){
3595 var t = this.findTargetItem(e);
3598 // if(t == this.activeItem && t.shouldDeactivate(e)){
3599 // this.activeItem.deactivate();
3600 // delete this.activeItem;
3603 this.fireEvent("mouseout", this, e, t);
3608 * Displays this menu relative to another element
3609 * @param {String/HTMLElement/Roo.Element} element The element to align to
3610 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3611 * the element (defaults to this.defaultAlign)
3612 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3614 show : function(el, pos, parentMenu)
3616 if (false === this.fireEvent("beforeshow", this)) {
3617 Roo.log("show canceled");
3620 this.parentMenu = parentMenu;
3625 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3628 * Displays this menu at a specific xy position
3629 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3630 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3632 showAt : function(xy, parentMenu, /* private: */_e){
3633 this.parentMenu = parentMenu;
3638 this.fireEvent("beforeshow", this);
3639 //xy = this.el.adjustForConstraints(xy);
3643 this.hideMenuItems();
3644 this.hidden = false;
3645 this.triggerEl.addClass('open');
3646 this.el.addClass('show');
3648 // reassign x when hitting right
3649 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3650 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3653 // reassign y when hitting bottom
3654 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3655 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3658 // but the list may align on trigger left or trigger top... should it be a properity?
3660 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3665 this.fireEvent("show", this);
3671 this.doFocus.defer(50, this);
3675 doFocus : function(){
3677 this.focusEl.focus();
3682 * Hides this menu and optionally all parent menus
3683 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3685 hide : function(deep)
3687 if (false === this.fireEvent("beforehide", this)) {
3688 Roo.log("hide canceled");
3691 this.hideMenuItems();
3692 if(this.el && this.isVisible()){
3694 if(this.activeItem){
3695 this.activeItem.deactivate();
3696 this.activeItem = null;
3698 this.triggerEl.removeClass('open');;
3699 this.el.removeClass('show');
3701 this.fireEvent("hide", this);
3703 if(deep === true && this.parentMenu){
3704 this.parentMenu.hide(true);
3708 onTriggerClick : function(e)
3710 Roo.log('trigger click');
3712 var target = e.getTarget();
3714 Roo.log(target.nodeName.toLowerCase());
3716 if(target.nodeName.toLowerCase() === 'i'){
3722 onTriggerPress : function(e)
3724 Roo.log('trigger press');
3725 //Roo.log(e.getTarget());
3726 // Roo.log(this.triggerEl.dom);
3728 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3729 var pel = Roo.get(e.getTarget());
3730 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3731 Roo.log('is treeview or dropdown?');
3735 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3739 if (this.isVisible()) {
3744 this.show(this.triggerEl, '?', false);
3747 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3754 hideMenuItems : function()
3756 Roo.log("hide Menu Items");
3761 this.el.select('.open',true).each(function(aa) {
3763 aa.removeClass('open');
3767 addxtypeChild : function (tree, cntr) {
3768 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3770 this.menuitems.add(comp);
3782 this.getEl().dom.innerHTML = '';
3783 this.menuitems.clear();
3797 * @class Roo.bootstrap.MenuItem
3798 * @extends Roo.bootstrap.Component
3799 * Bootstrap MenuItem class
3800 * @cfg {String} html the menu label
3801 * @cfg {String} href the link
3802 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3803 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3804 * @cfg {Boolean} active used on sidebars to highlight active itesm
3805 * @cfg {String} fa favicon to show on left of menu item.
3806 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3810 * Create a new MenuItem
3811 * @param {Object} config The config object
3815 Roo.bootstrap.MenuItem = function(config){
3816 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3821 * The raw click event for the entire grid.
3822 * @param {Roo.bootstrap.MenuItem} this
3823 * @param {Roo.EventObject} e
3829 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3833 preventDefault: false,
3834 isContainer : false,
3838 getAutoCreate : function(){
3840 if(this.isContainer){
3843 cls: 'dropdown-menu-item '
3853 cls : 'dropdown-item',
3858 if (this.fa !== false) {
3861 cls : 'fa fa-' + this.fa
3870 cls: 'dropdown-menu-item',
3873 if (this.parent().type == 'treeview') {
3874 cfg.cls = 'treeview-menu';
3877 cfg.cls += ' active';
3882 anc.href = this.href || cfg.cn[0].href ;
3883 ctag.html = this.html || cfg.cn[0].html ;
3887 initEvents: function()
3889 if (this.parent().type == 'treeview') {
3890 this.el.select('a').on('click', this.onClick, this);
3894 this.menu.parentType = this.xtype;
3895 this.menu.triggerEl = this.el;
3896 this.menu = this.addxtype(Roo.apply({}, this.menu));
3900 onClick : function(e)
3902 Roo.log('item on click ');
3904 if(this.preventDefault){
3907 //this.parent().hideMenuItems();
3909 this.fireEvent('click', this, e);
3928 * @class Roo.bootstrap.MenuSeparator
3929 * @extends Roo.bootstrap.Component
3930 * Bootstrap MenuSeparator class
3933 * Create a new MenuItem
3934 * @param {Object} config The config object
3938 Roo.bootstrap.MenuSeparator = function(config){
3939 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3942 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3944 getAutoCreate : function(){
3963 * @class Roo.bootstrap.Modal
3964 * @extends Roo.bootstrap.Component
3965 * Bootstrap Modal class
3966 * @cfg {String} title Title of dialog
3967 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3968 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3969 * @cfg {Boolean} specificTitle default false
3970 * @cfg {Array} buttons Array of buttons or standard button set..
3971 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3972 * @cfg {Boolean} animate default true
3973 * @cfg {Boolean} allow_close default true
3974 * @cfg {Boolean} fitwindow default false
3975 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3976 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3977 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3978 * @cfg {String} size (sm|lg|xl) default empty
3979 * @cfg {Number} max_width set the max width of modal
3980 * @cfg {Boolean} editableTitle can the title be edited
3985 * Create a new Modal Dialog
3986 * @param {Object} config The config object
3989 Roo.bootstrap.Modal = function(config){
3990 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3995 * The raw btnclick event for the button
3996 * @param {Roo.EventObject} e
4001 * Fire when dialog resize
4002 * @param {Roo.bootstrap.Modal} this
4003 * @param {Roo.EventObject} e
4007 * @event titlechanged
4008 * Fire when the editable title has been changed
4009 * @param {Roo.bootstrap.Modal} this
4010 * @param {Roo.EventObject} value
4012 "titlechanged" : true
4015 this.buttons = this.buttons || [];
4018 this.tmpl = Roo.factory(this.tmpl);
4023 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4025 title : 'test dialog',
4035 specificTitle: false,
4037 buttonPosition: 'right',
4059 editableTitle : false,
4061 onRender : function(ct, position)
4063 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4066 var cfg = Roo.apply({}, this.getAutoCreate());
4069 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4071 //if (!cfg.name.length) {
4075 cfg.cls += ' ' + this.cls;
4078 cfg.style = this.style;
4080 this.el = Roo.get(document.body).createChild(cfg, position);
4082 //var type = this.el.dom.type;
4085 if(this.tabIndex !== undefined){
4086 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4089 this.dialogEl = this.el.select('.modal-dialog',true).first();
4090 this.bodyEl = this.el.select('.modal-body',true).first();
4091 this.closeEl = this.el.select('.modal-header .close', true).first();
4092 this.headerEl = this.el.select('.modal-header',true).first();
4093 this.titleEl = this.el.select('.modal-title',true).first();
4094 this.footerEl = this.el.select('.modal-footer',true).first();
4096 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4098 //this.el.addClass("x-dlg-modal");
4100 if (this.buttons.length) {
4101 Roo.each(this.buttons, function(bb) {
4102 var b = Roo.apply({}, bb);
4103 b.xns = b.xns || Roo.bootstrap;
4104 b.xtype = b.xtype || 'Button';
4105 if (typeof(b.listeners) == 'undefined') {
4106 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4109 var btn = Roo.factory(b);
4111 btn.render(this.getButtonContainer());
4115 // render the children.
4118 if(typeof(this.items) != 'undefined'){
4119 var items = this.items;
4122 for(var i =0;i < items.length;i++) {
4123 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4127 this.items = nitems;
4129 // where are these used - they used to be body/close/footer
4133 //this.el.addClass([this.fieldClass, this.cls]);
4137 getAutoCreate : function()
4139 // we will default to modal-body-overflow - might need to remove or make optional later.
4141 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4142 html : this.html || ''
4147 cls : 'modal-title',
4151 if(this.specificTitle){ // WTF is this?
4156 if (this.allow_close && Roo.bootstrap.version == 3) {
4166 if (this.editableTitle) {
4168 cls: 'form-control roo-editable-title d-none',
4174 if (this.allow_close && Roo.bootstrap.version == 4) {
4184 if(this.size.length){
4185 size = 'modal-' + this.size;
4188 var footer = Roo.bootstrap.version == 3 ?
4190 cls : 'modal-footer',
4194 cls: 'btn-' + this.buttonPosition
4199 { // BS4 uses mr-auto on left buttons....
4200 cls : 'modal-footer'
4211 cls: "modal-dialog " + size,
4214 cls : "modal-content",
4217 cls : 'modal-header',
4232 modal.cls += ' fade';
4238 getChildContainer : function() {
4243 getButtonContainer : function() {
4245 return Roo.bootstrap.version == 4 ?
4246 this.el.select('.modal-footer',true).first()
4247 : this.el.select('.modal-footer div',true).first();
4250 initEvents : function()
4252 if (this.allow_close) {
4253 this.closeEl.on('click', this.hide, this);
4255 Roo.EventManager.onWindowResize(this.resize, this, true);
4256 if (this.editableTitle) {
4257 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4258 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4259 this.headerEditEl.on('keyup', function(e) {
4260 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4261 this.toggleHeaderInput(false)
4264 this.headerEditEl.on('blur', function(e) {
4265 this.toggleHeaderInput(false)
4274 this.maskEl.setSize(
4275 Roo.lib.Dom.getViewWidth(true),
4276 Roo.lib.Dom.getViewHeight(true)
4279 if (this.fitwindow) {
4281 this.dialogEl.setStyle( { 'max-width' : '100%' });
4283 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4284 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4289 if(this.max_width !== 0) {
4291 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4294 this.setSize(w, this.height);
4298 if(this.max_height) {
4299 this.setSize(w,Math.min(
4301 Roo.lib.Dom.getViewportHeight(true) - 60
4307 if(!this.fit_content) {
4308 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4312 this.setSize(w, Math.min(
4314 this.headerEl.getHeight() +
4315 this.footerEl.getHeight() +
4316 this.getChildHeight(this.bodyEl.dom.childNodes),
4317 Roo.lib.Dom.getViewportHeight(true) - 60)
4323 setSize : function(w,h)
4334 if (!this.rendered) {
4337 this.toggleHeaderInput(false);
4338 //this.el.setStyle('display', 'block');
4339 this.el.removeClass('hideing');
4340 this.el.dom.style.display='block';
4342 Roo.get(document.body).addClass('modal-open');
4344 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4347 this.el.addClass('show');
4348 this.el.addClass('in');
4351 this.el.addClass('show');
4352 this.el.addClass('in');
4355 // not sure how we can show data in here..
4357 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4360 Roo.get(document.body).addClass("x-body-masked");
4362 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4363 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4364 this.maskEl.dom.style.display = 'block';
4365 this.maskEl.addClass('show');
4370 this.fireEvent('show', this);
4372 // set zindex here - otherwise it appears to be ignored...
4373 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4376 this.items.forEach( function(e) {
4377 e.layout ? e.layout() : false;
4385 if(this.fireEvent("beforehide", this) !== false){
4387 this.maskEl.removeClass('show');
4389 this.maskEl.dom.style.display = '';
4390 Roo.get(document.body).removeClass("x-body-masked");
4391 this.el.removeClass('in');
4392 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4394 if(this.animate){ // why
4395 this.el.addClass('hideing');
4396 this.el.removeClass('show');
4398 if (!this.el.hasClass('hideing')) {
4399 return; // it's been shown again...
4402 this.el.dom.style.display='';
4404 Roo.get(document.body).removeClass('modal-open');
4405 this.el.removeClass('hideing');
4409 this.el.removeClass('show');
4410 this.el.dom.style.display='';
4411 Roo.get(document.body).removeClass('modal-open');
4414 this.fireEvent('hide', this);
4417 isVisible : function()
4420 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4424 addButton : function(str, cb)
4428 var b = Roo.apply({}, { html : str } );
4429 b.xns = b.xns || Roo.bootstrap;
4430 b.xtype = b.xtype || 'Button';
4431 if (typeof(b.listeners) == 'undefined') {
4432 b.listeners = { click : cb.createDelegate(this) };
4435 var btn = Roo.factory(b);
4437 btn.render(this.getButtonContainer());
4443 setDefaultButton : function(btn)
4445 //this.el.select('.modal-footer').()
4448 resizeTo: function(w,h)
4450 this.dialogEl.setWidth(w);
4452 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4454 this.bodyEl.setHeight(h - diff);
4456 this.fireEvent('resize', this);
4459 setContentSize : function(w, h)
4463 onButtonClick: function(btn,e)
4466 this.fireEvent('btnclick', btn.name, e);
4469 * Set the title of the Dialog
4470 * @param {String} str new Title
4472 setTitle: function(str) {
4473 this.titleEl.dom.innerHTML = str;
4477 * Set the body of the Dialog
4478 * @param {String} str new Title
4480 setBody: function(str) {
4481 this.bodyEl.dom.innerHTML = str;
4484 * Set the body of the Dialog using the template
4485 * @param {Obj} data - apply this data to the template and replace the body contents.
4487 applyBody: function(obj)
4490 Roo.log("Error - using apply Body without a template");
4493 this.tmpl.overwrite(this.bodyEl, obj);
4496 getChildHeight : function(child_nodes)
4500 child_nodes.length == 0
4505 var child_height = 0;
4507 for(var i = 0; i < child_nodes.length; i++) {
4510 * for modal with tabs...
4511 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4513 var layout_childs = child_nodes[i].childNodes;
4515 for(var j = 0; j < layout_childs.length; j++) {
4517 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4519 var layout_body_childs = layout_childs[j].childNodes;
4521 for(var k = 0; k < layout_body_childs.length; k++) {
4523 if(layout_body_childs[k].classList.contains('navbar')) {
4524 child_height += layout_body_childs[k].offsetHeight;
4528 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4530 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4532 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4534 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4535 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4550 child_height += child_nodes[i].offsetHeight;
4551 // Roo.log(child_nodes[i].offsetHeight);
4554 return child_height;
4556 toggleHeaderInput : function(is_edit)
4558 if (!this.editableTitle) {
4559 return; // not editable.
4561 if (is_edit && this.is_header_editing) {
4562 return; // already editing..
4566 this.headerEditEl.dom.value = this.title;
4567 this.headerEditEl.removeClass('d-none');
4568 this.headerEditEl.dom.focus();
4569 this.titleEl.addClass('d-none');
4571 this.is_header_editing = true;
4574 // flip back to not editing.
4575 this.title = this.headerEditEl.dom.value;
4576 this.headerEditEl.addClass('d-none');
4577 this.titleEl.removeClass('d-none');
4578 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4579 this.is_header_editing = false;
4580 this.fireEvent('titlechanged', this, this.title);
4589 Roo.apply(Roo.bootstrap.Modal, {
4591 * Button config that displays a single OK button
4600 * Button config that displays Yes and No buttons
4616 * Button config that displays OK and Cancel buttons
4631 * Button config that displays Yes, No and Cancel buttons
4656 * messagebox - can be used as a replace
4660 * @class Roo.MessageBox
4661 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4665 Roo.Msg.alert('Status', 'Changes saved successfully.');
4667 // Prompt for user data:
4668 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4670 // process text value...
4674 // Show a dialog using config options:
4676 title:'Save Changes?',
4677 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4678 buttons: Roo.Msg.YESNOCANCEL,
4685 Roo.bootstrap.MessageBox = function(){
4686 var dlg, opt, mask, waitTimer;
4687 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4688 var buttons, activeTextEl, bwidth;
4692 var handleButton = function(button){
4694 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4698 var handleHide = function(){
4700 dlg.el.removeClass(opt.cls);
4703 // Roo.TaskMgr.stop(waitTimer);
4704 // waitTimer = null;
4709 var updateButtons = function(b){
4712 buttons["ok"].hide();
4713 buttons["cancel"].hide();
4714 buttons["yes"].hide();
4715 buttons["no"].hide();
4716 dlg.footerEl.hide();
4720 dlg.footerEl.show();
4721 for(var k in buttons){
4722 if(typeof buttons[k] != "function"){
4725 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4726 width += buttons[k].el.getWidth()+15;
4736 var handleEsc = function(d, k, e){
4737 if(opt && opt.closable !== false){
4747 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4748 * @return {Roo.BasicDialog} The BasicDialog element
4750 getDialog : function(){
4752 dlg = new Roo.bootstrap.Modal( {
4755 //constraintoviewport:false,
4757 //collapsible : false,
4762 //buttonAlign:"center",
4763 closeClick : function(){
4764 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4767 handleButton("cancel");
4772 dlg.on("hide", handleHide);
4774 //dlg.addKeyListener(27, handleEsc);
4776 this.buttons = buttons;
4777 var bt = this.buttonText;
4778 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4779 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4780 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4781 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4783 bodyEl = dlg.bodyEl.createChild({
4785 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4786 '<textarea class="roo-mb-textarea"></textarea>' +
4787 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4789 msgEl = bodyEl.dom.firstChild;
4790 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4791 textboxEl.enableDisplayMode();
4792 textboxEl.addKeyListener([10,13], function(){
4793 if(dlg.isVisible() && opt && opt.buttons){
4796 }else if(opt.buttons.yes){
4797 handleButton("yes");
4801 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4802 textareaEl.enableDisplayMode();
4803 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4804 progressEl.enableDisplayMode();
4806 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4807 var pf = progressEl.dom.firstChild;
4809 pp = Roo.get(pf.firstChild);
4810 pp.setHeight(pf.offsetHeight);
4818 * Updates the message box body text
4819 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4820 * the XHTML-compliant non-breaking space character '&#160;')
4821 * @return {Roo.MessageBox} This message box
4823 updateText : function(text)
4825 if(!dlg.isVisible() && !opt.width){
4826 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4827 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4829 msgEl.innerHTML = text || ' ';
4831 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4832 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4834 Math.min(opt.width || cw , this.maxWidth),
4835 Math.max(opt.minWidth || this.minWidth, bwidth)
4838 activeTextEl.setWidth(w);
4840 if(dlg.isVisible()){
4841 dlg.fixedcenter = false;
4843 // to big, make it scroll. = But as usual stupid IE does not support
4846 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4847 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4848 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4850 bodyEl.dom.style.height = '';
4851 bodyEl.dom.style.overflowY = '';
4854 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4856 bodyEl.dom.style.overflowX = '';
4859 dlg.setContentSize(w, bodyEl.getHeight());
4860 if(dlg.isVisible()){
4861 dlg.fixedcenter = true;
4867 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4868 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4869 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4870 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4871 * @return {Roo.MessageBox} This message box
4873 updateProgress : function(value, text){
4875 this.updateText(text);
4878 if (pp) { // weird bug on my firefox - for some reason this is not defined
4879 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4880 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4886 * Returns true if the message box is currently displayed
4887 * @return {Boolean} True if the message box is visible, else false
4889 isVisible : function(){
4890 return dlg && dlg.isVisible();
4894 * Hides the message box if it is displayed
4897 if(this.isVisible()){
4903 * Displays a new message box, or reinitializes an existing message box, based on the config options
4904 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4905 * The following config object properties are supported:
4907 Property Type Description
4908 ---------- --------------- ------------------------------------------------------------------------------------
4909 animEl String/Element An id or Element from which the message box should animate as it opens and
4910 closes (defaults to undefined)
4911 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4912 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4913 closable Boolean False to hide the top-right close button (defaults to true). Note that
4914 progress and wait dialogs will ignore this property and always hide the
4915 close button as they can only be closed programmatically.
4916 cls String A custom CSS class to apply to the message box element
4917 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4918 displayed (defaults to 75)
4919 fn Function A callback function to execute after closing the dialog. The arguments to the
4920 function will be btn (the name of the button that was clicked, if applicable,
4921 e.g. "ok"), and text (the value of the active text field, if applicable).
4922 Progress and wait dialogs will ignore this option since they do not respond to
4923 user actions and can only be closed programmatically, so any required function
4924 should be called by the same code after it closes the dialog.
4925 icon String A CSS class that provides a background image to be used as an icon for
4926 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4927 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4928 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4929 modal Boolean False to allow user interaction with the page while the message box is
4930 displayed (defaults to true)
4931 msg String A string that will replace the existing message box body text (defaults
4932 to the XHTML-compliant non-breaking space character ' ')
4933 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4934 progress Boolean True to display a progress bar (defaults to false)
4935 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4936 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4937 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4938 title String The title text
4939 value String The string value to set into the active textbox element if displayed
4940 wait Boolean True to display a progress bar (defaults to false)
4941 width Number The width of the dialog in pixels
4948 msg: 'Please enter your address:',
4950 buttons: Roo.MessageBox.OKCANCEL,
4953 animEl: 'addAddressBtn'
4956 * @param {Object} config Configuration options
4957 * @return {Roo.MessageBox} This message box
4959 show : function(options)
4962 // this causes nightmares if you show one dialog after another
4963 // especially on callbacks..
4965 if(this.isVisible()){
4968 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4969 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4970 Roo.log("New Dialog Message:" + options.msg )
4971 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4972 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4975 var d = this.getDialog();
4977 d.setTitle(opt.title || " ");
4978 d.closeEl.setDisplayed(opt.closable !== false);
4979 activeTextEl = textboxEl;
4980 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4985 textareaEl.setHeight(typeof opt.multiline == "number" ?
4986 opt.multiline : this.defaultTextHeight);
4987 activeTextEl = textareaEl;
4996 progressEl.setDisplayed(opt.progress === true);
4998 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5000 this.updateProgress(0);
5001 activeTextEl.dom.value = opt.value || "";
5003 dlg.setDefaultButton(activeTextEl);
5005 var bs = opt.buttons;
5009 }else if(bs && bs.yes){
5010 db = buttons["yes"];
5012 dlg.setDefaultButton(db);
5014 bwidth = updateButtons(opt.buttons);
5015 this.updateText(opt.msg);
5017 d.el.addClass(opt.cls);
5019 d.proxyDrag = opt.proxyDrag === true;
5020 d.modal = opt.modal !== false;
5021 d.mask = opt.modal !== false ? mask : false;
5023 // force it to the end of the z-index stack so it gets a cursor in FF
5024 document.body.appendChild(dlg.el.dom);
5025 d.animateTarget = null;
5026 d.show(options.animEl);
5032 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5033 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5034 * and closing the message box when the process is complete.
5035 * @param {String} title The title bar text
5036 * @param {String} msg The message box body text
5037 * @return {Roo.MessageBox} This message box
5039 progress : function(title, msg){
5046 minWidth: this.minProgressWidth,
5053 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5054 * If a callback function is passed it will be called after the user clicks the button, and the
5055 * id of the button that was clicked will be passed as the only parameter to the callback
5056 * (could also be the top-right close button).
5057 * @param {String} title The title bar text
5058 * @param {String} msg The message box body text
5059 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5060 * @param {Object} scope (optional) The scope of the callback function
5061 * @return {Roo.MessageBox} This message box
5063 alert : function(title, msg, fn, scope)
5078 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5079 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5080 * You are responsible for closing the message box when the process is complete.
5081 * @param {String} msg The message box body text
5082 * @param {String} title (optional) The title bar text
5083 * @return {Roo.MessageBox} This message box
5085 wait : function(msg, title){
5096 waitTimer = Roo.TaskMgr.start({
5098 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5106 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5107 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5108 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5109 * @param {String} title The title bar text
5110 * @param {String} msg The message box body text
5111 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5112 * @param {Object} scope (optional) The scope of the callback function
5113 * @return {Roo.MessageBox} This message box
5115 confirm : function(title, msg, fn, scope){
5119 buttons: this.YESNO,
5128 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5129 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5130 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5131 * (could also be the top-right close button) and the text that was entered will be passed as the two
5132 * parameters to the callback.
5133 * @param {String} title The title bar text
5134 * @param {String} msg The message box body text
5135 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5136 * @param {Object} scope (optional) The scope of the callback function
5137 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5138 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5139 * @return {Roo.MessageBox} This message box
5141 prompt : function(title, msg, fn, scope, multiline){
5145 buttons: this.OKCANCEL,
5150 multiline: multiline,
5157 * Button config that displays a single OK button
5162 * Button config that displays Yes and No buttons
5165 YESNO : {yes:true, no:true},
5167 * Button config that displays OK and Cancel buttons
5170 OKCANCEL : {ok:true, cancel:true},
5172 * Button config that displays Yes, No and Cancel buttons
5175 YESNOCANCEL : {yes:true, no:true, cancel:true},
5178 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5181 defaultTextHeight : 75,
5183 * The maximum width in pixels of the message box (defaults to 600)
5188 * The minimum width in pixels of the message box (defaults to 100)
5193 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5194 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5197 minProgressWidth : 250,
5199 * An object containing the default button text strings that can be overriden for localized language support.
5200 * Supported properties are: ok, cancel, yes and no.
5201 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5214 * Shorthand for {@link Roo.MessageBox}
5216 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5217 Roo.Msg = Roo.Msg || Roo.MessageBox;
5226 * @class Roo.bootstrap.Navbar
5227 * @extends Roo.bootstrap.Component
5228 * Bootstrap Navbar class
5231 * Create a new Navbar
5232 * @param {Object} config The config object
5236 Roo.bootstrap.Navbar = function(config){
5237 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5241 * @event beforetoggle
5242 * Fire before toggle the menu
5243 * @param {Roo.EventObject} e
5245 "beforetoggle" : true
5249 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5258 getAutoCreate : function(){
5261 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5265 initEvents :function ()
5267 //Roo.log(this.el.select('.navbar-toggle',true));
5268 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5275 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5277 var size = this.el.getSize();
5278 this.maskEl.setSize(size.width, size.height);
5279 this.maskEl.enableDisplayMode("block");
5288 getChildContainer : function()
5290 if (this.el && this.el.select('.collapse').getCount()) {
5291 return this.el.select('.collapse',true).first();
5306 onToggle : function()
5309 if(this.fireEvent('beforetoggle', this) === false){
5312 var ce = this.el.select('.navbar-collapse',true).first();
5314 if (!ce.hasClass('show')) {
5324 * Expand the navbar pulldown
5326 expand : function ()
5329 var ce = this.el.select('.navbar-collapse',true).first();
5330 if (ce.hasClass('collapsing')) {
5333 ce.dom.style.height = '';
5335 ce.addClass('in'); // old...
5336 ce.removeClass('collapse');
5337 ce.addClass('show');
5338 var h = ce.getHeight();
5340 ce.removeClass('show');
5341 // at this point we should be able to see it..
5342 ce.addClass('collapsing');
5344 ce.setHeight(0); // resize it ...
5345 ce.on('transitionend', function() {
5346 //Roo.log('done transition');
5347 ce.removeClass('collapsing');
5348 ce.addClass('show');
5349 ce.removeClass('collapse');
5351 ce.dom.style.height = '';
5352 }, this, { single: true} );
5354 ce.dom.scrollTop = 0;
5357 * Collapse the navbar pulldown
5359 collapse : function()
5361 var ce = this.el.select('.navbar-collapse',true).first();
5363 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5364 // it's collapsed or collapsing..
5367 ce.removeClass('in'); // old...
5368 ce.setHeight(ce.getHeight());
5369 ce.removeClass('show');
5370 ce.addClass('collapsing');
5372 ce.on('transitionend', function() {
5373 ce.dom.style.height = '';
5374 ce.removeClass('collapsing');
5375 ce.addClass('collapse');
5376 }, this, { single: true} );
5396 * @class Roo.bootstrap.NavSimplebar
5397 * @extends Roo.bootstrap.Navbar
5398 * Bootstrap Sidebar class
5400 * @cfg {Boolean} inverse is inverted color
5402 * @cfg {String} type (nav | pills | tabs)
5403 * @cfg {Boolean} arrangement stacked | justified
5404 * @cfg {String} align (left | right) alignment
5406 * @cfg {Boolean} main (true|false) main nav bar? default false
5407 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5409 * @cfg {String} tag (header|footer|nav|div) default is nav
5411 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5415 * Create a new Sidebar
5416 * @param {Object} config The config object
5420 Roo.bootstrap.NavSimplebar = function(config){
5421 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5424 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5440 getAutoCreate : function(){
5444 tag : this.tag || 'div',
5445 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5447 if (['light','white'].indexOf(this.weight) > -1) {
5448 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5450 cfg.cls += ' bg-' + this.weight;
5453 cfg.cls += ' navbar-inverse';
5457 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5459 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5468 cls: 'nav nav-' + this.xtype,
5474 this.type = this.type || 'nav';
5475 if (['tabs','pills'].indexOf(this.type) != -1) {
5476 cfg.cn[0].cls += ' nav-' + this.type
5480 if (this.type!=='nav') {
5481 Roo.log('nav type must be nav/tabs/pills')
5483 cfg.cn[0].cls += ' navbar-nav'
5489 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5490 cfg.cn[0].cls += ' nav-' + this.arrangement;
5494 if (this.align === 'right') {
5495 cfg.cn[0].cls += ' navbar-right';
5520 * navbar-expand-md fixed-top
5524 * @class Roo.bootstrap.NavHeaderbar
5525 * @extends Roo.bootstrap.NavSimplebar
5526 * Bootstrap Sidebar class
5528 * @cfg {String} brand what is brand
5529 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5530 * @cfg {String} brand_href href of the brand
5531 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5532 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5533 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5534 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5537 * Create a new Sidebar
5538 * @param {Object} config The config object
5542 Roo.bootstrap.NavHeaderbar = function(config){
5543 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5547 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5554 desktopCenter : false,
5557 getAutoCreate : function(){
5560 tag: this.nav || 'nav',
5561 cls: 'navbar navbar-expand-md',
5567 if (this.desktopCenter) {
5568 cn.push({cls : 'container', cn : []});
5576 cls: 'navbar-toggle navbar-toggler',
5577 'data-toggle': 'collapse',
5582 html: 'Toggle navigation'
5586 cls: 'icon-bar navbar-toggler-icon'
5599 cn.push( Roo.bootstrap.version == 4 ? btn : {
5601 cls: 'navbar-header',
5610 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5614 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5616 if (['light','white'].indexOf(this.weight) > -1) {
5617 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5619 cfg.cls += ' bg-' + this.weight;
5622 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5623 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5625 // tag can override this..
5627 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5630 if (this.brand !== '') {
5631 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5632 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5634 href: this.brand_href ? this.brand_href : '#',
5635 cls: 'navbar-brand',
5643 cfg.cls += ' main-nav';
5651 getHeaderChildContainer : function()
5653 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5654 return this.el.select('.navbar-header',true).first();
5657 return this.getChildContainer();
5660 getChildContainer : function()
5663 return this.el.select('.roo-navbar-collapse',true).first();
5668 initEvents : function()
5670 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5672 if (this.autohide) {
5677 Roo.get(document).on('scroll',function(e) {
5678 var ns = Roo.get(document).getScroll().top;
5679 var os = prevScroll;
5683 ft.removeClass('slideDown');
5684 ft.addClass('slideUp');
5687 ft.removeClass('slideUp');
5688 ft.addClass('slideDown');
5709 * @class Roo.bootstrap.NavSidebar
5710 * @extends Roo.bootstrap.Navbar
5711 * Bootstrap Sidebar class
5714 * Create a new Sidebar
5715 * @param {Object} config The config object
5719 Roo.bootstrap.NavSidebar = function(config){
5720 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5723 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5725 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5727 getAutoCreate : function(){
5732 cls: 'sidebar sidebar-nav'
5754 * @class Roo.bootstrap.NavGroup
5755 * @extends Roo.bootstrap.Component
5756 * Bootstrap NavGroup class
5757 * @cfg {String} align (left|right)
5758 * @cfg {Boolean} inverse
5759 * @cfg {String} type (nav|pills|tab) default nav
5760 * @cfg {String} navId - reference Id for navbar.
5761 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5764 * Create a new nav group
5765 * @param {Object} config The config object
5768 Roo.bootstrap.NavGroup = function(config){
5769 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5772 Roo.bootstrap.NavGroup.register(this);
5776 * Fires when the active item changes
5777 * @param {Roo.bootstrap.NavGroup} this
5778 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5779 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5786 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5798 getAutoCreate : function()
5800 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5806 if (Roo.bootstrap.version == 4) {
5807 if (['tabs','pills'].indexOf(this.type) != -1) {
5808 cfg.cls += ' nav-' + this.type;
5810 // trying to remove so header bar can right align top?
5811 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5812 // do not use on header bar...
5813 cfg.cls += ' navbar-nav';
5818 if (['tabs','pills'].indexOf(this.type) != -1) {
5819 cfg.cls += ' nav-' + this.type
5821 if (this.type !== 'nav') {
5822 Roo.log('nav type must be nav/tabs/pills')
5824 cfg.cls += ' navbar-nav'
5828 if (this.parent() && this.parent().sidebar) {
5831 cls: 'dashboard-menu sidebar-menu'
5837 if (this.form === true) {
5840 cls: 'navbar-form form-inline'
5842 //nav navbar-right ml-md-auto
5843 if (this.align === 'right') {
5844 cfg.cls += ' navbar-right ml-md-auto';
5846 cfg.cls += ' navbar-left';
5850 if (this.align === 'right') {
5851 cfg.cls += ' navbar-right ml-md-auto';
5853 cfg.cls += ' mr-auto';
5857 cfg.cls += ' navbar-inverse';
5865 * sets the active Navigation item
5866 * @param {Roo.bootstrap.NavItem} the new current navitem
5868 setActiveItem : function(item)
5871 Roo.each(this.navItems, function(v){
5876 v.setActive(false, true);
5883 item.setActive(true, true);
5884 this.fireEvent('changed', this, item, prev);
5889 * gets the active Navigation item
5890 * @return {Roo.bootstrap.NavItem} the current navitem
5892 getActive : function()
5896 Roo.each(this.navItems, function(v){
5907 indexOfNav : function()
5911 Roo.each(this.navItems, function(v,i){
5922 * adds a Navigation item
5923 * @param {Roo.bootstrap.NavItem} the navitem to add
5925 addItem : function(cfg)
5927 if (this.form && Roo.bootstrap.version == 4) {
5930 var cn = new Roo.bootstrap.NavItem(cfg);
5932 cn.parentId = this.id;
5933 cn.onRender(this.el, null);
5937 * register a Navigation item
5938 * @param {Roo.bootstrap.NavItem} the navitem to add
5940 register : function(item)
5942 this.navItems.push( item);
5943 item.navId = this.navId;
5948 * clear all the Navigation item
5951 clearAll : function()
5954 this.el.dom.innerHTML = '';
5957 getNavItem: function(tabId)
5960 Roo.each(this.navItems, function(e) {
5961 if (e.tabId == tabId) {
5971 setActiveNext : function()
5973 var i = this.indexOfNav(this.getActive());
5974 if (i > this.navItems.length) {
5977 this.setActiveItem(this.navItems[i+1]);
5979 setActivePrev : function()
5981 var i = this.indexOfNav(this.getActive());
5985 this.setActiveItem(this.navItems[i-1]);
5987 clearWasActive : function(except) {
5988 Roo.each(this.navItems, function(e) {
5989 if (e.tabId != except.tabId && e.was_active) {
5990 e.was_active = false;
5997 getWasActive : function ()
6000 Roo.each(this.navItems, function(e) {
6015 Roo.apply(Roo.bootstrap.NavGroup, {
6019 * register a Navigation Group
6020 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6022 register : function(navgrp)
6024 this.groups[navgrp.navId] = navgrp;
6028 * fetch a Navigation Group based on the navigation ID
6029 * @param {string} the navgroup to add
6030 * @returns {Roo.bootstrap.NavGroup} the navgroup
6032 get: function(navId) {
6033 if (typeof(this.groups[navId]) == 'undefined') {
6035 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6037 return this.groups[navId] ;
6052 * @class Roo.bootstrap.NavItem
6053 * @extends Roo.bootstrap.Component
6054 * Bootstrap Navbar.NavItem class
6055 * @cfg {String} href link to
6056 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6057 * @cfg {Boolean} button_outline show and outlined button
6058 * @cfg {String} html content of button
6059 * @cfg {String} badge text inside badge
6060 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6061 * @cfg {String} glyphicon DEPRICATED - use fa
6062 * @cfg {String} icon DEPRICATED - use fa
6063 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6064 * @cfg {Boolean} active Is item active
6065 * @cfg {Boolean} disabled Is item disabled
6066 * @cfg {String} linkcls Link Class
6067 * @cfg {Boolean} preventDefault (true | false) default false
6068 * @cfg {String} tabId the tab that this item activates.
6069 * @cfg {String} tagtype (a|span) render as a href or span?
6070 * @cfg {Boolean} animateRef (true|false) link to element default false
6073 * Create a new Navbar Item
6074 * @param {Object} config The config object
6076 Roo.bootstrap.NavItem = function(config){
6077 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6082 * The raw click event for the entire grid.
6083 * @param {Roo.EventObject} e
6088 * Fires when the active item active state changes
6089 * @param {Roo.bootstrap.NavItem} this
6090 * @param {boolean} state the new state
6096 * Fires when scroll to element
6097 * @param {Roo.bootstrap.NavItem} this
6098 * @param {Object} options
6099 * @param {Roo.EventObject} e
6107 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6116 preventDefault : false,
6124 button_outline : false,
6128 getAutoCreate : function(){
6135 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6138 cfg.cls += ' active' ;
6140 if (this.disabled) {
6141 cfg.cls += ' disabled';
6145 if (this.button_weight.length) {
6146 cfg.tag = this.href ? 'a' : 'button';
6147 cfg.html = this.html || '';
6148 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6150 cfg.href = this.href;
6153 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6156 // menu .. should add dropdown-menu class - so no need for carat..
6158 if (this.badge !== '') {
6160 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6165 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6169 href : this.href || "#",
6170 html: this.html || ''
6173 if (this.tagtype == 'a') {
6174 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6178 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6181 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6183 if(this.glyphicon) {
6184 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6189 cfg.cn[0].html += " <span class='caret'></span>";
6193 if (this.badge !== '') {
6195 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6203 onRender : function(ct, position)
6205 // Roo.log("Call onRender: " + this.xtype);
6206 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6210 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6211 this.navLink = this.el.select('.nav-link',true).first();
6216 initEvents: function()
6218 if (typeof (this.menu) != 'undefined') {
6219 this.menu.parentType = this.xtype;
6220 this.menu.triggerEl = this.el;
6221 this.menu = this.addxtype(Roo.apply({}, this.menu));
6224 this.el.on('click', this.onClick, this);
6226 //if(this.tagtype == 'span'){
6227 // this.el.select('span',true).on('click', this.onClick, this);
6230 // at this point parent should be available..
6231 this.parent().register(this);
6234 onClick : function(e)
6236 if (e.getTarget('.dropdown-menu-item')) {
6237 // did you click on a menu itemm.... - then don't trigger onclick..
6242 this.preventDefault ||
6245 Roo.log("NavItem - prevent Default?");
6249 if (this.disabled) {
6253 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6254 if (tg && tg.transition) {
6255 Roo.log("waiting for the transitionend");
6261 //Roo.log("fire event clicked");
6262 if(this.fireEvent('click', this, e) === false){
6266 if(this.tagtype == 'span'){
6270 //Roo.log(this.href);
6271 var ael = this.el.select('a',true).first();
6274 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6275 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6276 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6277 return; // ignore... - it's a 'hash' to another page.
6279 Roo.log("NavItem - prevent Default?");
6281 this.scrollToElement(e);
6285 var p = this.parent();
6287 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6288 if (typeof(p.setActiveItem) !== 'undefined') {
6289 p.setActiveItem(this);
6293 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6294 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6295 // remove the collapsed menu expand...
6296 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6300 isActive: function () {
6303 setActive : function(state, fire, is_was_active)
6305 if (this.active && !state && this.navId) {
6306 this.was_active = true;
6307 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6309 nv.clearWasActive(this);
6313 this.active = state;
6316 this.el.removeClass('active');
6317 this.navLink ? this.navLink.removeClass('active') : false;
6318 } else if (!this.el.hasClass('active')) {
6320 this.el.addClass('active');
6321 if (Roo.bootstrap.version == 4 && this.navLink ) {
6322 this.navLink.addClass('active');
6327 this.fireEvent('changed', this, state);
6330 // show a panel if it's registered and related..
6332 if (!this.navId || !this.tabId || !state || is_was_active) {
6336 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6340 var pan = tg.getPanelByName(this.tabId);
6344 // if we can not flip to new panel - go back to old nav highlight..
6345 if (false == tg.showPanel(pan)) {
6346 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6348 var onav = nv.getWasActive();
6350 onav.setActive(true, false, true);
6359 // this should not be here...
6360 setDisabled : function(state)
6362 this.disabled = state;
6364 this.el.removeClass('disabled');
6365 } else if (!this.el.hasClass('disabled')) {
6366 this.el.addClass('disabled');
6372 * Fetch the element to display the tooltip on.
6373 * @return {Roo.Element} defaults to this.el
6375 tooltipEl : function()
6377 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6380 scrollToElement : function(e)
6382 var c = document.body;
6385 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6387 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6388 c = document.documentElement;
6391 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6397 var o = target.calcOffsetsTo(c);
6404 this.fireEvent('scrollto', this, options, e);
6406 Roo.get(c).scrollTo('top', options.value, true);
6419 * <span> icon </span>
6420 * <span> text </span>
6421 * <span>badge </span>
6425 * @class Roo.bootstrap.NavSidebarItem
6426 * @extends Roo.bootstrap.NavItem
6427 * Bootstrap Navbar.NavSidebarItem class
6428 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6429 * {Boolean} open is the menu open
6430 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6431 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6432 * {String} buttonSize (sm|md|lg)the extra classes for the button
6433 * {Boolean} showArrow show arrow next to the text (default true)
6435 * Create a new Navbar Button
6436 * @param {Object} config The config object
6438 Roo.bootstrap.NavSidebarItem = function(config){
6439 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6444 * The raw click event for the entire grid.
6445 * @param {Roo.EventObject} e
6450 * Fires when the active item active state changes
6451 * @param {Roo.bootstrap.NavSidebarItem} this
6452 * @param {boolean} state the new state
6460 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6462 badgeWeight : 'default',
6468 buttonWeight : 'default',
6474 getAutoCreate : function(){
6479 href : this.href || '#',
6485 if(this.buttonView){
6488 href : this.href || '#',
6489 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6502 cfg.cls += ' active';
6505 if (this.disabled) {
6506 cfg.cls += ' disabled';
6509 cfg.cls += ' open x-open';
6512 if (this.glyphicon || this.icon) {
6513 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6514 a.cn.push({ tag : 'i', cls : c }) ;
6517 if(!this.buttonView){
6520 html : this.html || ''
6527 if (this.badge !== '') {
6528 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6534 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6537 a.cls += ' dropdown-toggle treeview' ;
6543 initEvents : function()
6545 if (typeof (this.menu) != 'undefined') {
6546 this.menu.parentType = this.xtype;
6547 this.menu.triggerEl = this.el;
6548 this.menu = this.addxtype(Roo.apply({}, this.menu));
6551 this.el.on('click', this.onClick, this);
6553 if(this.badge !== ''){
6554 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6559 onClick : function(e)
6566 if(this.preventDefault){
6570 this.fireEvent('click', this, e);
6573 disable : function()
6575 this.setDisabled(true);
6580 this.setDisabled(false);
6583 setDisabled : function(state)
6585 if(this.disabled == state){
6589 this.disabled = state;
6592 this.el.addClass('disabled');
6596 this.el.removeClass('disabled');
6601 setActive : function(state)
6603 if(this.active == state){
6607 this.active = state;
6610 this.el.addClass('active');
6614 this.el.removeClass('active');
6619 isActive: function ()
6624 setBadge : function(str)
6630 this.badgeEl.dom.innerHTML = str;
6645 Roo.namespace('Roo.bootstrap.breadcrumb');
6649 * @class Roo.bootstrap.breadcrumb.Nav
6650 * @extends Roo.bootstrap.Component
6651 * Bootstrap Breadcrumb Nav Class
6653 * @children Roo.bootstrap.breadcrumb.Item
6656 * Create a new breadcrumb.Nav
6657 * @param {Object} config The config object
6661 Roo.bootstrap.breadcrumb.Nav = function(config){
6662 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6667 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6669 getAutoCreate : function()
6686 initEvents: function()
6688 this.olEl = this.el.select('ol',true).first();
6690 getChildContainer : function()
6706 * @class Roo.bootstrap.breadcrumb.Nav
6707 * @extends Roo.bootstrap.Component
6708 * Bootstrap Breadcrumb Nav Class
6710 * @children Roo.bootstrap.breadcrumb.Component
6711 * @cfg {String} html the content of the link.
6712 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6713 * @cfg {Boolean} active is it active
6717 * Create a new breadcrumb.Nav
6718 * @param {Object} config The config object
6721 Roo.bootstrap.breadcrumb.Item = function(config){
6722 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6727 * The img click event for the img.
6728 * @param {Roo.EventObject} e
6735 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6740 getAutoCreate : function()
6745 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6747 if (this.href !== false) {
6754 cfg.html = this.html;
6760 initEvents: function()
6763 this.el.select('a', true).first().on('click',this.onClick, this)
6767 onClick : function(e)
6770 this.fireEvent('click',this, e);
6783 * @class Roo.bootstrap.Row
6784 * @extends Roo.bootstrap.Component
6785 * Bootstrap Row class (contains columns...)
6789 * @param {Object} config The config object
6792 Roo.bootstrap.Row = function(config){
6793 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6796 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6798 getAutoCreate : function(){
6817 * @class Roo.bootstrap.Pagination
6818 * @extends Roo.bootstrap.Component
6819 * Bootstrap Pagination class
6820 * @cfg {String} size xs | sm | md | lg
6821 * @cfg {Boolean} inverse false | true
6824 * Create a new Pagination
6825 * @param {Object} config The config object
6828 Roo.bootstrap.Pagination = function(config){
6829 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6838 getAutoCreate : function(){
6844 cfg.cls += ' inverse';
6850 cfg.cls += " " + this.cls;
6868 * @class Roo.bootstrap.PaginationItem
6869 * @extends Roo.bootstrap.Component
6870 * Bootstrap PaginationItem class
6871 * @cfg {String} html text
6872 * @cfg {String} href the link
6873 * @cfg {Boolean} preventDefault (true | false) default true
6874 * @cfg {Boolean} active (true | false) default false
6875 * @cfg {Boolean} disabled default false
6879 * Create a new PaginationItem
6880 * @param {Object} config The config object
6884 Roo.bootstrap.PaginationItem = function(config){
6885 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6890 * The raw click event for the entire grid.
6891 * @param {Roo.EventObject} e
6897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6901 preventDefault: true,
6906 getAutoCreate : function(){
6912 href : this.href ? this.href : '#',
6913 html : this.html ? this.html : ''
6923 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6927 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6933 initEvents: function() {
6935 this.el.on('click', this.onClick, this);
6938 onClick : function(e)
6940 Roo.log('PaginationItem on click ');
6941 if(this.preventDefault){
6949 this.fireEvent('click', this, e);
6965 * @class Roo.bootstrap.Slider
6966 * @extends Roo.bootstrap.Component
6967 * Bootstrap Slider class
6970 * Create a new Slider
6971 * @param {Object} config The config object
6974 Roo.bootstrap.Slider = function(config){
6975 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6980 getAutoCreate : function(){
6984 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6988 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7000 * Ext JS Library 1.1.1
7001 * Copyright(c) 2006-2007, Ext JS, LLC.
7003 * Originally Released Under LGPL - original licence link has changed is not relivant.
7006 * <script type="text/javascript">
7011 * @class Roo.grid.ColumnModel
7012 * @extends Roo.util.Observable
7013 * This is the default implementation of a ColumnModel used by the Grid. It defines
7014 * the columns in the grid.
7017 var colModel = new Roo.grid.ColumnModel([
7018 {header: "Ticker", width: 60, sortable: true, locked: true},
7019 {header: "Company Name", width: 150, sortable: true},
7020 {header: "Market Cap.", width: 100, sortable: true},
7021 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7022 {header: "Employees", width: 100, sortable: true, resizable: false}
7027 * The config options listed for this class are options which may appear in each
7028 * individual column definition.
7029 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7031 * @param {Object} config An Array of column config objects. See this class's
7032 * config objects for details.
7034 Roo.grid.ColumnModel = function(config){
7036 * The config passed into the constructor
7038 this.config = config;
7041 // if no id, create one
7042 // if the column does not have a dataIndex mapping,
7043 // map it to the order it is in the config
7044 for(var i = 0, len = config.length; i < len; i++){
7046 if(typeof c.dataIndex == "undefined"){
7049 if(typeof c.renderer == "string"){
7050 c.renderer = Roo.util.Format[c.renderer];
7052 if(typeof c.id == "undefined"){
7055 if(c.editor && c.editor.xtype){
7056 c.editor = Roo.factory(c.editor, Roo.grid);
7058 if(c.editor && c.editor.isFormField){
7059 c.editor = new Roo.grid.GridEditor(c.editor);
7061 this.lookup[c.id] = c;
7065 * The width of columns which have no width specified (defaults to 100)
7068 this.defaultWidth = 100;
7071 * Default sortable of columns which have no sortable specified (defaults to false)
7074 this.defaultSortable = false;
7078 * @event widthchange
7079 * Fires when the width of a column changes.
7080 * @param {ColumnModel} this
7081 * @param {Number} columnIndex The column index
7082 * @param {Number} newWidth The new width
7084 "widthchange": true,
7086 * @event headerchange
7087 * Fires when the text of a header changes.
7088 * @param {ColumnModel} this
7089 * @param {Number} columnIndex The column index
7090 * @param {Number} newText The new header text
7092 "headerchange": true,
7094 * @event hiddenchange
7095 * Fires when a column is hidden or "unhidden".
7096 * @param {ColumnModel} this
7097 * @param {Number} columnIndex The column index
7098 * @param {Boolean} hidden true if hidden, false otherwise
7100 "hiddenchange": true,
7102 * @event columnmoved
7103 * Fires when a column is moved.
7104 * @param {ColumnModel} this
7105 * @param {Number} oldIndex
7106 * @param {Number} newIndex
7108 "columnmoved" : true,
7110 * @event columlockchange
7111 * Fires when a column's locked state is changed
7112 * @param {ColumnModel} this
7113 * @param {Number} colIndex
7114 * @param {Boolean} locked true if locked
7116 "columnlockchange" : true
7118 Roo.grid.ColumnModel.superclass.constructor.call(this);
7120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7122 * @cfg {String} header The header text to display in the Grid view.
7125 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7126 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7127 * specified, the column's index is used as an index into the Record's data Array.
7130 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7131 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7134 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7135 * Defaults to the value of the {@link #defaultSortable} property.
7136 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7139 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7142 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7145 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7148 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7151 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7152 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7153 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7154 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7157 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7160 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7163 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7166 * @cfg {String} cursor (Optional)
7169 * @cfg {String} tooltip (Optional)
7172 * @cfg {Number} xs (Optional)
7175 * @cfg {Number} sm (Optional)
7178 * @cfg {Number} md (Optional)
7181 * @cfg {Number} lg (Optional)
7184 * Returns the id of the column at the specified index.
7185 * @param {Number} index The column index
7186 * @return {String} the id
7188 getColumnId : function(index){
7189 return this.config[index].id;
7193 * Returns the column for a specified id.
7194 * @param {String} id The column id
7195 * @return {Object} the column
7197 getColumnById : function(id){
7198 return this.lookup[id];
7203 * Returns the column for a specified dataIndex.
7204 * @param {String} dataIndex The column dataIndex
7205 * @return {Object|Boolean} the column or false if not found
7207 getColumnByDataIndex: function(dataIndex){
7208 var index = this.findColumnIndex(dataIndex);
7209 return index > -1 ? this.config[index] : false;
7213 * Returns the index for a specified column id.
7214 * @param {String} id The column id
7215 * @return {Number} the index, or -1 if not found
7217 getIndexById : function(id){
7218 for(var i = 0, len = this.config.length; i < len; i++){
7219 if(this.config[i].id == id){
7227 * Returns the index for a specified column dataIndex.
7228 * @param {String} dataIndex The column dataIndex
7229 * @return {Number} the index, or -1 if not found
7232 findColumnIndex : function(dataIndex){
7233 for(var i = 0, len = this.config.length; i < len; i++){
7234 if(this.config[i].dataIndex == dataIndex){
7242 moveColumn : function(oldIndex, newIndex){
7243 var c = this.config[oldIndex];
7244 this.config.splice(oldIndex, 1);
7245 this.config.splice(newIndex, 0, c);
7246 this.dataMap = null;
7247 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7250 isLocked : function(colIndex){
7251 return this.config[colIndex].locked === true;
7254 setLocked : function(colIndex, value, suppressEvent){
7255 if(this.isLocked(colIndex) == value){
7258 this.config[colIndex].locked = value;
7260 this.fireEvent("columnlockchange", this, colIndex, value);
7264 getTotalLockedWidth : function(){
7266 for(var i = 0; i < this.config.length; i++){
7267 if(this.isLocked(i) && !this.isHidden(i)){
7268 this.totalWidth += this.getColumnWidth(i);
7274 getLockedCount : function(){
7275 for(var i = 0, len = this.config.length; i < len; i++){
7276 if(!this.isLocked(i)){
7281 return this.config.length;
7285 * Returns the number of columns.
7288 getColumnCount : function(visibleOnly){
7289 if(visibleOnly === true){
7291 for(var i = 0, len = this.config.length; i < len; i++){
7292 if(!this.isHidden(i)){
7298 return this.config.length;
7302 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7303 * @param {Function} fn
7304 * @param {Object} scope (optional)
7305 * @return {Array} result
7307 getColumnsBy : function(fn, scope){
7309 for(var i = 0, len = this.config.length; i < len; i++){
7310 var c = this.config[i];
7311 if(fn.call(scope||this, c, i) === true){
7319 * Returns true if the specified column is sortable.
7320 * @param {Number} col The column index
7323 isSortable : function(col){
7324 if(typeof this.config[col].sortable == "undefined"){
7325 return this.defaultSortable;
7327 return this.config[col].sortable;
7331 * Returns the rendering (formatting) function defined for the column.
7332 * @param {Number} col The column index.
7333 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7335 getRenderer : function(col){
7336 if(!this.config[col].renderer){
7337 return Roo.grid.ColumnModel.defaultRenderer;
7339 return this.config[col].renderer;
7343 * Sets the rendering (formatting) function for a column.
7344 * @param {Number} col The column index
7345 * @param {Function} fn The function to use to process the cell's raw data
7346 * to return HTML markup for the grid view. The render function is called with
7347 * the following parameters:<ul>
7348 * <li>Data value.</li>
7349 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7350 * <li>css A CSS style string to apply to the table cell.</li>
7351 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7352 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7353 * <li>Row index</li>
7354 * <li>Column index</li>
7355 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7357 setRenderer : function(col, fn){
7358 this.config[col].renderer = fn;
7362 * Returns the width for the specified column.
7363 * @param {Number} col The column index
7366 getColumnWidth : function(col){
7367 return this.config[col].width * 1 || this.defaultWidth;
7371 * Sets the width for a column.
7372 * @param {Number} col The column index
7373 * @param {Number} width The new width
7375 setColumnWidth : function(col, width, suppressEvent){
7376 this.config[col].width = width;
7377 this.totalWidth = null;
7379 this.fireEvent("widthchange", this, col, width);
7384 * Returns the total width of all columns.
7385 * @param {Boolean} includeHidden True to include hidden column widths
7388 getTotalWidth : function(includeHidden){
7389 if(!this.totalWidth){
7390 this.totalWidth = 0;
7391 for(var i = 0, len = this.config.length; i < len; i++){
7392 if(includeHidden || !this.isHidden(i)){
7393 this.totalWidth += this.getColumnWidth(i);
7397 return this.totalWidth;
7401 * Returns the header for the specified column.
7402 * @param {Number} col The column index
7405 getColumnHeader : function(col){
7406 return this.config[col].header;
7410 * Sets the header for a column.
7411 * @param {Number} col The column index
7412 * @param {String} header The new header
7414 setColumnHeader : function(col, header){
7415 this.config[col].header = header;
7416 this.fireEvent("headerchange", this, col, header);
7420 * Returns the tooltip for the specified column.
7421 * @param {Number} col The column index
7424 getColumnTooltip : function(col){
7425 return this.config[col].tooltip;
7428 * Sets the tooltip for a column.
7429 * @param {Number} col The column index
7430 * @param {String} tooltip The new tooltip
7432 setColumnTooltip : function(col, tooltip){
7433 this.config[col].tooltip = tooltip;
7437 * Returns the dataIndex for the specified column.
7438 * @param {Number} col The column index
7441 getDataIndex : function(col){
7442 return this.config[col].dataIndex;
7446 * Sets the dataIndex for a column.
7447 * @param {Number} col The column index
7448 * @param {Number} dataIndex The new dataIndex
7450 setDataIndex : function(col, dataIndex){
7451 this.config[col].dataIndex = dataIndex;
7457 * Returns true if the cell is editable.
7458 * @param {Number} colIndex The column index
7459 * @param {Number} rowIndex The row index - this is nto actually used..?
7462 isCellEditable : function(colIndex, rowIndex){
7463 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7467 * Returns the editor defined for the cell/column.
7468 * return false or null to disable editing.
7469 * @param {Number} colIndex The column index
7470 * @param {Number} rowIndex The row index
7473 getCellEditor : function(colIndex, rowIndex){
7474 return this.config[colIndex].editor;
7478 * Sets if a column is editable.
7479 * @param {Number} col The column index
7480 * @param {Boolean} editable True if the column is editable
7482 setEditable : function(col, editable){
7483 this.config[col].editable = editable;
7488 * Returns true if the column is hidden.
7489 * @param {Number} colIndex The column index
7492 isHidden : function(colIndex){
7493 return this.config[colIndex].hidden;
7498 * Returns true if the column width cannot be changed
7500 isFixed : function(colIndex){
7501 return this.config[colIndex].fixed;
7505 * Returns true if the column can be resized
7508 isResizable : function(colIndex){
7509 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7512 * Sets if a column is hidden.
7513 * @param {Number} colIndex The column index
7514 * @param {Boolean} hidden True if the column is hidden
7516 setHidden : function(colIndex, hidden){
7517 this.config[colIndex].hidden = hidden;
7518 this.totalWidth = null;
7519 this.fireEvent("hiddenchange", this, colIndex, hidden);
7523 * Sets the editor for a column.
7524 * @param {Number} col The column index
7525 * @param {Object} editor The editor object
7527 setEditor : function(col, editor){
7528 this.config[col].editor = editor;
7532 Roo.grid.ColumnModel.defaultRenderer = function(value)
7534 if(typeof value == "object") {
7537 if(typeof value == "string" && value.length < 1){
7541 return String.format("{0}", value);
7544 // Alias for backwards compatibility
7545 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7548 * Ext JS Library 1.1.1
7549 * Copyright(c) 2006-2007, Ext JS, LLC.
7551 * Originally Released Under LGPL - original licence link has changed is not relivant.
7554 * <script type="text/javascript">
7558 * @class Roo.LoadMask
7559 * A simple utility class for generically masking elements while loading data. If the element being masked has
7560 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7561 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7562 * element's UpdateManager load indicator and will be destroyed after the initial load.
7564 * Create a new LoadMask
7565 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7566 * @param {Object} config The config object
7568 Roo.LoadMask = function(el, config){
7569 this.el = Roo.get(el);
7570 Roo.apply(this, config);
7572 this.store.on('beforeload', this.onBeforeLoad, this);
7573 this.store.on('load', this.onLoad, this);
7574 this.store.on('loadexception', this.onLoadException, this);
7575 this.removeMask = false;
7577 var um = this.el.getUpdateManager();
7578 um.showLoadIndicator = false; // disable the default indicator
7579 um.on('beforeupdate', this.onBeforeLoad, this);
7580 um.on('update', this.onLoad, this);
7581 um.on('failure', this.onLoad, this);
7582 this.removeMask = true;
7586 Roo.LoadMask.prototype = {
7588 * @cfg {Boolean} removeMask
7589 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7590 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7594 * The text to display in a centered loading message box (defaults to 'Loading...')
7598 * @cfg {String} msgCls
7599 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7601 msgCls : 'x-mask-loading',
7604 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7610 * Disables the mask to prevent it from being displayed
7612 disable : function(){
7613 this.disabled = true;
7617 * Enables the mask so that it can be displayed
7619 enable : function(){
7620 this.disabled = false;
7623 onLoadException : function()
7627 if (typeof(arguments[3]) != 'undefined') {
7628 Roo.MessageBox.alert("Error loading",arguments[3]);
7632 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7633 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7640 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7645 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7649 onBeforeLoad : function(){
7651 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7656 destroy : function(){
7658 this.store.un('beforeload', this.onBeforeLoad, this);
7659 this.store.un('load', this.onLoad, this);
7660 this.store.un('loadexception', this.onLoadException, this);
7662 var um = this.el.getUpdateManager();
7663 um.un('beforeupdate', this.onBeforeLoad, this);
7664 um.un('update', this.onLoad, this);
7665 um.un('failure', this.onLoad, this);
7676 * @class Roo.bootstrap.Table
7677 * @extends Roo.bootstrap.Component
7678 * Bootstrap Table class
7679 * @cfg {String} cls table class
7680 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7681 * @cfg {String} bgcolor Specifies the background color for a table
7682 * @cfg {Number} border Specifies whether the table cells should have borders or not
7683 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7684 * @cfg {Number} cellspacing Specifies the space between cells
7685 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7686 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7687 * @cfg {String} sortable Specifies that the table should be sortable
7688 * @cfg {String} summary Specifies a summary of the content of a table
7689 * @cfg {Number} width Specifies the width of a table
7690 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7692 * @cfg {boolean} striped Should the rows be alternative striped
7693 * @cfg {boolean} bordered Add borders to the table
7694 * @cfg {boolean} hover Add hover highlighting
7695 * @cfg {boolean} condensed Format condensed
7696 * @cfg {boolean} responsive Format condensed
7697 * @cfg {Boolean} loadMask (true|false) default false
7698 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7699 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7700 * @cfg {Boolean} rowSelection (true|false) default false
7701 * @cfg {Boolean} cellSelection (true|false) default false
7702 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7703 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7704 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7705 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7709 * Create a new Table
7710 * @param {Object} config The config object
7713 Roo.bootstrap.Table = function(config){
7714 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7719 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7720 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7721 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7722 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7724 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7726 this.sm.grid = this;
7727 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7728 this.sm = this.selModel;
7729 this.sm.xmodule = this.xmodule || false;
7732 if (this.cm && typeof(this.cm.config) == 'undefined') {
7733 this.colModel = new Roo.grid.ColumnModel(this.cm);
7734 this.cm = this.colModel;
7735 this.cm.xmodule = this.xmodule || false;
7738 this.store= Roo.factory(this.store, Roo.data);
7739 this.ds = this.store;
7740 this.ds.xmodule = this.xmodule || false;
7743 if (this.footer && this.store) {
7744 this.footer.dataSource = this.ds;
7745 this.footer = Roo.factory(this.footer);
7752 * Fires when a cell is clicked
7753 * @param {Roo.bootstrap.Table} this
7754 * @param {Roo.Element} el
7755 * @param {Number} rowIndex
7756 * @param {Number} columnIndex
7757 * @param {Roo.EventObject} e
7761 * @event celldblclick
7762 * Fires when a cell is double clicked
7763 * @param {Roo.bootstrap.Table} this
7764 * @param {Roo.Element} el
7765 * @param {Number} rowIndex
7766 * @param {Number} columnIndex
7767 * @param {Roo.EventObject} e
7769 "celldblclick" : true,
7772 * Fires when a row is clicked
7773 * @param {Roo.bootstrap.Table} this
7774 * @param {Roo.Element} el
7775 * @param {Number} rowIndex
7776 * @param {Roo.EventObject} e
7780 * @event rowdblclick
7781 * Fires when a row is double clicked
7782 * @param {Roo.bootstrap.Table} this
7783 * @param {Roo.Element} el
7784 * @param {Number} rowIndex
7785 * @param {Roo.EventObject} e
7787 "rowdblclick" : true,
7790 * Fires when a mouseover occur
7791 * @param {Roo.bootstrap.Table} this
7792 * @param {Roo.Element} el
7793 * @param {Number} rowIndex
7794 * @param {Number} columnIndex
7795 * @param {Roo.EventObject} e
7800 * Fires when a mouseout occur
7801 * @param {Roo.bootstrap.Table} this
7802 * @param {Roo.Element} el
7803 * @param {Number} rowIndex
7804 * @param {Number} columnIndex
7805 * @param {Roo.EventObject} e
7810 * Fires when a row is rendered, so you can change add a style to it.
7811 * @param {Roo.bootstrap.Table} this
7812 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7816 * @event rowsrendered
7817 * Fires when all the rows have been rendered
7818 * @param {Roo.bootstrap.Table} this
7820 'rowsrendered' : true,
7822 * @event contextmenu
7823 * The raw contextmenu event for the entire grid.
7824 * @param {Roo.EventObject} e
7826 "contextmenu" : true,
7828 * @event rowcontextmenu
7829 * Fires when a row is right clicked
7830 * @param {Roo.bootstrap.Table} this
7831 * @param {Number} rowIndex
7832 * @param {Roo.EventObject} e
7834 "rowcontextmenu" : true,
7836 * @event cellcontextmenu
7837 * Fires when a cell is right clicked
7838 * @param {Roo.bootstrap.Table} this
7839 * @param {Number} rowIndex
7840 * @param {Number} cellIndex
7841 * @param {Roo.EventObject} e
7843 "cellcontextmenu" : true,
7845 * @event headercontextmenu
7846 * Fires when a header is right clicked
7847 * @param {Roo.bootstrap.Table} this
7848 * @param {Number} columnIndex
7849 * @param {Roo.EventObject} e
7851 "headercontextmenu" : true
7855 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7881 rowSelection : false,
7882 cellSelection : false,
7885 // Roo.Element - the tbody
7887 // Roo.Element - thead element
7890 container: false, // used by gridpanel...
7896 auto_hide_footer : false,
7898 getAutoCreate : function()
7900 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7907 if (this.scrollBody) {
7908 cfg.cls += ' table-body-fixed';
7911 cfg.cls += ' table-striped';
7915 cfg.cls += ' table-hover';
7917 if (this.bordered) {
7918 cfg.cls += ' table-bordered';
7920 if (this.condensed) {
7921 cfg.cls += ' table-condensed';
7923 if (this.responsive) {
7924 cfg.cls += ' table-responsive';
7928 cfg.cls+= ' ' +this.cls;
7931 // this lot should be simplifed...
7944 ].forEach(function(k) {
7952 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7955 if(this.store || this.cm){
7956 if(this.headerShow){
7957 cfg.cn.push(this.renderHeader());
7960 cfg.cn.push(this.renderBody());
7962 if(this.footerShow){
7963 cfg.cn.push(this.renderFooter());
7965 // where does this come from?
7966 //cfg.cls+= ' TableGrid';
7969 return { cn : [ cfg ] };
7972 initEvents : function()
7974 if(!this.store || !this.cm){
7977 if (this.selModel) {
7978 this.selModel.initEvents();
7982 //Roo.log('initEvents with ds!!!!');
7984 this.mainBody = this.el.select('tbody', true).first();
7985 this.mainHead = this.el.select('thead', true).first();
7986 this.mainFoot = this.el.select('tfoot', true).first();
7992 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7993 e.on('click', _this.sort, _this);
7996 this.mainBody.on("click", this.onClick, this);
7997 this.mainBody.on("dblclick", this.onDblClick, this);
7999 // why is this done????? = it breaks dialogs??
8000 //this.parent().el.setStyle('position', 'relative');
8004 this.footer.parentId = this.id;
8005 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8008 this.el.select('tfoot tr td').first().addClass('hide');
8013 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8016 this.store.on('load', this.onLoad, this);
8017 this.store.on('beforeload', this.onBeforeLoad, this);
8018 this.store.on('update', this.onUpdate, this);
8019 this.store.on('add', this.onAdd, this);
8020 this.store.on("clear", this.clear, this);
8022 this.el.on("contextmenu", this.onContextMenu, this);
8024 this.mainBody.on('scroll', this.onBodyScroll, this);
8026 this.cm.on("headerchange", this.onHeaderChange, this);
8028 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8032 onContextMenu : function(e, t)
8034 this.processEvent("contextmenu", e);
8037 processEvent : function(name, e)
8039 if (name != 'touchstart' ) {
8040 this.fireEvent(name, e);
8043 var t = e.getTarget();
8045 var cell = Roo.get(t);
8051 if(cell.findParent('tfoot', false, true)){
8055 if(cell.findParent('thead', false, true)){
8057 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8058 cell = Roo.get(t).findParent('th', false, true);
8060 Roo.log("failed to find th in thead?");
8061 Roo.log(e.getTarget());
8066 var cellIndex = cell.dom.cellIndex;
8068 var ename = name == 'touchstart' ? 'click' : name;
8069 this.fireEvent("header" + ename, this, cellIndex, e);
8074 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8075 cell = Roo.get(t).findParent('td', false, true);
8077 Roo.log("failed to find th in tbody?");
8078 Roo.log(e.getTarget());
8083 var row = cell.findParent('tr', false, true);
8084 var cellIndex = cell.dom.cellIndex;
8085 var rowIndex = row.dom.rowIndex - 1;
8089 this.fireEvent("row" + name, this, rowIndex, e);
8093 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8099 onMouseover : function(e, el)
8101 var cell = Roo.get(el);
8107 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8108 cell = cell.findParent('td', false, true);
8111 var row = cell.findParent('tr', false, true);
8112 var cellIndex = cell.dom.cellIndex;
8113 var rowIndex = row.dom.rowIndex - 1; // start from 0
8115 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8119 onMouseout : function(e, el)
8121 var cell = Roo.get(el);
8127 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8128 cell = cell.findParent('td', false, true);
8131 var row = cell.findParent('tr', false, true);
8132 var cellIndex = cell.dom.cellIndex;
8133 var rowIndex = row.dom.rowIndex - 1; // start from 0
8135 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8139 onClick : function(e, el)
8141 var cell = Roo.get(el);
8143 if(!cell || (!this.cellSelection && !this.rowSelection)){
8147 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8148 cell = cell.findParent('td', false, true);
8151 if(!cell || typeof(cell) == 'undefined'){
8155 var row = cell.findParent('tr', false, true);
8157 if(!row || typeof(row) == 'undefined'){
8161 var cellIndex = cell.dom.cellIndex;
8162 var rowIndex = this.getRowIndex(row);
8164 // why??? - should these not be based on SelectionModel?
8165 if(this.cellSelection){
8166 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8169 if(this.rowSelection){
8170 this.fireEvent('rowclick', this, row, rowIndex, e);
8176 onDblClick : function(e,el)
8178 var cell = Roo.get(el);
8180 if(!cell || (!this.cellSelection && !this.rowSelection)){
8184 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8185 cell = cell.findParent('td', false, true);
8188 if(!cell || typeof(cell) == 'undefined'){
8192 var row = cell.findParent('tr', false, true);
8194 if(!row || typeof(row) == 'undefined'){
8198 var cellIndex = cell.dom.cellIndex;
8199 var rowIndex = this.getRowIndex(row);
8201 if(this.cellSelection){
8202 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8205 if(this.rowSelection){
8206 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8210 sort : function(e,el)
8212 var col = Roo.get(el);
8214 if(!col.hasClass('sortable')){
8218 var sort = col.attr('sort');
8221 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8225 this.store.sortInfo = {field : sort, direction : dir};
8228 Roo.log("calling footer first");
8229 this.footer.onClick('first');
8232 this.store.load({ params : { start : 0 } });
8236 renderHeader : function()
8244 this.totalWidth = 0;
8246 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8248 var config = cm.config[i];
8252 cls : 'x-hcol-' + i,
8254 html: cm.getColumnHeader(i)
8259 if(typeof(config.sortable) != 'undefined' && config.sortable){
8261 c.html = '<i class="glyphicon"></i>' + c.html;
8264 // could use BS4 hidden-..-down
8266 if(typeof(config.lgHeader) != 'undefined'){
8267 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8270 if(typeof(config.mdHeader) != 'undefined'){
8271 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8274 if(typeof(config.smHeader) != 'undefined'){
8275 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8278 if(typeof(config.xsHeader) != 'undefined'){
8279 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8286 if(typeof(config.tooltip) != 'undefined'){
8287 c.tooltip = config.tooltip;
8290 if(typeof(config.colspan) != 'undefined'){
8291 c.colspan = config.colspan;
8294 if(typeof(config.hidden) != 'undefined' && config.hidden){
8295 c.style += ' display:none;';
8298 if(typeof(config.dataIndex) != 'undefined'){
8299 c.sort = config.dataIndex;
8304 if(typeof(config.align) != 'undefined' && config.align.length){
8305 c.style += ' text-align:' + config.align + ';';
8308 if(typeof(config.width) != 'undefined'){
8309 c.style += ' width:' + config.width + 'px;';
8310 this.totalWidth += config.width;
8312 this.totalWidth += 100; // assume minimum of 100 per column?
8315 if(typeof(config.cls) != 'undefined'){
8316 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8319 ['xs','sm','md','lg'].map(function(size){
8321 if(typeof(config[size]) == 'undefined'){
8325 if (!config[size]) { // 0 = hidden
8326 // BS 4 '0' is treated as hide that column and below.
8327 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8331 c.cls += ' col-' + size + '-' + config[size] + (
8332 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8344 renderBody : function()
8354 colspan : this.cm.getColumnCount()
8364 renderFooter : function()
8374 colspan : this.cm.getColumnCount()
8388 // Roo.log('ds onload');
8393 var ds = this.store;
8395 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8396 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8397 if (_this.store.sortInfo) {
8399 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8400 e.select('i', true).addClass(['glyphicon-arrow-up']);
8403 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8404 e.select('i', true).addClass(['glyphicon-arrow-down']);
8409 var tbody = this.mainBody;
8411 if(ds.getCount() > 0){
8412 ds.data.each(function(d,rowIndex){
8413 var row = this.renderRow(cm, ds, rowIndex);
8415 tbody.createChild(row);
8419 if(row.cellObjects.length){
8420 Roo.each(row.cellObjects, function(r){
8421 _this.renderCellObject(r);
8428 var tfoot = this.el.select('tfoot', true).first();
8430 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8432 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8434 var total = this.ds.getTotalCount();
8436 if(this.footer.pageSize < total){
8437 this.mainFoot.show();
8441 Roo.each(this.el.select('tbody td', true).elements, function(e){
8442 e.on('mouseover', _this.onMouseover, _this);
8445 Roo.each(this.el.select('tbody td', true).elements, function(e){
8446 e.on('mouseout', _this.onMouseout, _this);
8448 this.fireEvent('rowsrendered', this);
8454 onUpdate : function(ds,record)
8456 this.refreshRow(record);
8460 onRemove : function(ds, record, index, isUpdate){
8461 if(isUpdate !== true){
8462 this.fireEvent("beforerowremoved", this, index, record);
8464 var bt = this.mainBody.dom;
8466 var rows = this.el.select('tbody > tr', true).elements;
8468 if(typeof(rows[index]) != 'undefined'){
8469 bt.removeChild(rows[index].dom);
8472 // if(bt.rows[index]){
8473 // bt.removeChild(bt.rows[index]);
8476 if(isUpdate !== true){
8477 //this.stripeRows(index);
8478 //this.syncRowHeights(index, index);
8480 this.fireEvent("rowremoved", this, index, record);
8484 onAdd : function(ds, records, rowIndex)
8486 //Roo.log('on Add called');
8487 // - note this does not handle multiple adding very well..
8488 var bt = this.mainBody.dom;
8489 for (var i =0 ; i < records.length;i++) {
8490 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8491 //Roo.log(records[i]);
8492 //Roo.log(this.store.getAt(rowIndex+i));
8493 this.insertRow(this.store, rowIndex + i, false);
8500 refreshRow : function(record){
8501 var ds = this.store, index;
8502 if(typeof record == 'number'){
8504 record = ds.getAt(index);
8506 index = ds.indexOf(record);
8508 return; // should not happen - but seems to
8511 this.insertRow(ds, index, true);
8513 this.onRemove(ds, record, index+1, true);
8515 //this.syncRowHeights(index, index);
8517 this.fireEvent("rowupdated", this, index, record);
8520 insertRow : function(dm, rowIndex, isUpdate){
8523 this.fireEvent("beforerowsinserted", this, rowIndex);
8525 //var s = this.getScrollState();
8526 var row = this.renderRow(this.cm, this.store, rowIndex);
8527 // insert before rowIndex..
8528 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8532 if(row.cellObjects.length){
8533 Roo.each(row.cellObjects, function(r){
8534 _this.renderCellObject(r);
8539 this.fireEvent("rowsinserted", this, rowIndex);
8540 //this.syncRowHeights(firstRow, lastRow);
8541 //this.stripeRows(firstRow);
8548 getRowDom : function(rowIndex)
8550 var rows = this.el.select('tbody > tr', true).elements;
8552 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8555 // returns the object tree for a tr..
8558 renderRow : function(cm, ds, rowIndex)
8560 var d = ds.getAt(rowIndex);
8564 cls : 'x-row-' + rowIndex,
8568 var cellObjects = [];
8570 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8571 var config = cm.config[i];
8573 var renderer = cm.getRenderer(i);
8577 if(typeof(renderer) !== 'undefined'){
8578 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8580 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8581 // and are rendered into the cells after the row is rendered - using the id for the element.
8583 if(typeof(value) === 'object'){
8593 rowIndex : rowIndex,
8598 this.fireEvent('rowclass', this, rowcfg);
8602 cls : rowcfg.rowClass + ' x-col-' + i,
8604 html: (typeof(value) === 'object') ? '' : value
8611 if(typeof(config.colspan) != 'undefined'){
8612 td.colspan = config.colspan;
8615 if(typeof(config.hidden) != 'undefined' && config.hidden){
8616 td.style += ' display:none;';
8619 if(typeof(config.align) != 'undefined' && config.align.length){
8620 td.style += ' text-align:' + config.align + ';';
8622 if(typeof(config.valign) != 'undefined' && config.valign.length){
8623 td.style += ' vertical-align:' + config.valign + ';';
8626 if(typeof(config.width) != 'undefined'){
8627 td.style += ' width:' + config.width + 'px;';
8630 if(typeof(config.cursor) != 'undefined'){
8631 td.style += ' cursor:' + config.cursor + ';';
8634 if(typeof(config.cls) != 'undefined'){
8635 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8638 ['xs','sm','md','lg'].map(function(size){
8640 if(typeof(config[size]) == 'undefined'){
8646 if (!config[size]) { // 0 = hidden
8647 // BS 4 '0' is treated as hide that column and below.
8648 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8652 td.cls += ' col-' + size + '-' + config[size] + (
8653 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8663 row.cellObjects = cellObjects;
8671 onBeforeLoad : function()
8680 this.el.select('tbody', true).first().dom.innerHTML = '';
8683 * Show or hide a row.
8684 * @param {Number} rowIndex to show or hide
8685 * @param {Boolean} state hide
8687 setRowVisibility : function(rowIndex, state)
8689 var bt = this.mainBody.dom;
8691 var rows = this.el.select('tbody > tr', true).elements;
8693 if(typeof(rows[rowIndex]) == 'undefined'){
8696 rows[rowIndex].dom.style.display = state ? '' : 'none';
8700 getSelectionModel : function(){
8702 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8704 return this.selModel;
8707 * Render the Roo.bootstrap object from renderder
8709 renderCellObject : function(r)
8713 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8715 var t = r.cfg.render(r.container);
8718 Roo.each(r.cfg.cn, function(c){
8720 container: t.getChildContainer(),
8723 _this.renderCellObject(child);
8728 getRowIndex : function(row)
8732 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8743 * Returns the grid's underlying element = used by panel.Grid
8744 * @return {Element} The element
8746 getGridEl : function(){
8750 * Forces a resize - used by panel.Grid
8751 * @return {Element} The element
8753 autoSize : function()
8755 //var ctr = Roo.get(this.container.dom.parentElement);
8756 var ctr = Roo.get(this.el.dom);
8758 var thd = this.getGridEl().select('thead',true).first();
8759 var tbd = this.getGridEl().select('tbody', true).first();
8760 var tfd = this.getGridEl().select('tfoot', true).first();
8762 var cw = ctr.getWidth();
8763 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8767 tbd.setWidth(ctr.getWidth());
8768 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8769 // this needs fixing for various usage - currently only hydra job advers I think..
8771 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8773 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8776 cw = Math.max(cw, this.totalWidth);
8777 this.getGridEl().select('tbody tr',true).setWidth(cw);
8779 // resize 'expandable coloumn?
8781 return; // we doe not have a view in this design..
8784 onBodyScroll: function()
8786 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8788 this.mainHead.setStyle({
8789 'position' : 'relative',
8790 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8796 var scrollHeight = this.mainBody.dom.scrollHeight;
8798 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8800 var height = this.mainBody.getHeight();
8802 if(scrollHeight - height == scrollTop) {
8804 var total = this.ds.getTotalCount();
8806 if(this.footer.cursor + this.footer.pageSize < total){
8808 this.footer.ds.load({
8810 start : this.footer.cursor + this.footer.pageSize,
8811 limit : this.footer.pageSize
8821 onHeaderChange : function()
8823 var header = this.renderHeader();
8824 var table = this.el.select('table', true).first();
8826 this.mainHead.remove();
8827 this.mainHead = table.createChild(header, this.mainBody, false);
8830 onHiddenChange : function(colModel, colIndex, hidden)
8832 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8833 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8835 this.CSS.updateRule(thSelector, "display", "");
8836 this.CSS.updateRule(tdSelector, "display", "");
8839 this.CSS.updateRule(thSelector, "display", "none");
8840 this.CSS.updateRule(tdSelector, "display", "none");
8843 this.onHeaderChange();
8847 setColumnWidth: function(col_index, width)
8849 // width = "md-2 xs-2..."
8850 if(!this.colModel.config[col_index]) {
8854 var w = width.split(" ");
8856 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8858 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8861 for(var j = 0; j < w.length; j++) {
8867 var size_cls = w[j].split("-");
8869 if(!Number.isInteger(size_cls[1] * 1)) {
8873 if(!this.colModel.config[col_index][size_cls[0]]) {
8877 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8881 h_row[0].classList.replace(
8882 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8883 "col-"+size_cls[0]+"-"+size_cls[1]
8886 for(var i = 0; i < rows.length; i++) {
8888 var size_cls = w[j].split("-");
8890 if(!Number.isInteger(size_cls[1] * 1)) {
8894 if(!this.colModel.config[col_index][size_cls[0]]) {
8898 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8902 rows[i].classList.replace(
8903 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8904 "col-"+size_cls[0]+"-"+size_cls[1]
8908 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8923 * @class Roo.bootstrap.TableCell
8924 * @extends Roo.bootstrap.Component
8925 * Bootstrap TableCell class
8926 * @cfg {String} html cell contain text
8927 * @cfg {String} cls cell class
8928 * @cfg {String} tag cell tag (td|th) default td
8929 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8930 * @cfg {String} align Aligns the content in a cell
8931 * @cfg {String} axis Categorizes cells
8932 * @cfg {String} bgcolor Specifies the background color of a cell
8933 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8934 * @cfg {Number} colspan Specifies the number of columns a cell should span
8935 * @cfg {String} headers Specifies one or more header cells a cell is related to
8936 * @cfg {Number} height Sets the height of a cell
8937 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8938 * @cfg {Number} rowspan Sets the number of rows a cell should span
8939 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8940 * @cfg {String} valign Vertical aligns the content in a cell
8941 * @cfg {Number} width Specifies the width of a cell
8944 * Create a new TableCell
8945 * @param {Object} config The config object
8948 Roo.bootstrap.TableCell = function(config){
8949 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8952 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8972 getAutoCreate : function(){
8973 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8993 cfg.align=this.align
8999 cfg.bgcolor=this.bgcolor
9002 cfg.charoff=this.charoff
9005 cfg.colspan=this.colspan
9008 cfg.headers=this.headers
9011 cfg.height=this.height
9014 cfg.nowrap=this.nowrap
9017 cfg.rowspan=this.rowspan
9020 cfg.scope=this.scope
9023 cfg.valign=this.valign
9026 cfg.width=this.width
9045 * @class Roo.bootstrap.TableRow
9046 * @extends Roo.bootstrap.Component
9047 * Bootstrap TableRow class
9048 * @cfg {String} cls row class
9049 * @cfg {String} align Aligns the content in a table row
9050 * @cfg {String} bgcolor Specifies a background color for a table row
9051 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9052 * @cfg {String} valign Vertical aligns the content in a table row
9055 * Create a new TableRow
9056 * @param {Object} config The config object
9059 Roo.bootstrap.TableRow = function(config){
9060 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9063 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9071 getAutoCreate : function(){
9072 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9082 cfg.align = this.align;
9085 cfg.bgcolor = this.bgcolor;
9088 cfg.charoff = this.charoff;
9091 cfg.valign = this.valign;
9109 * @class Roo.bootstrap.TableBody
9110 * @extends Roo.bootstrap.Component
9111 * Bootstrap TableBody class
9112 * @cfg {String} cls element class
9113 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9114 * @cfg {String} align Aligns the content inside the element
9115 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9116 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9119 * Create a new TableBody
9120 * @param {Object} config The config object
9123 Roo.bootstrap.TableBody = function(config){
9124 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9127 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9135 getAutoCreate : function(){
9136 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9150 cfg.align = this.align;
9153 cfg.charoff = this.charoff;
9156 cfg.valign = this.valign;
9163 // initEvents : function()
9170 // this.store = Roo.factory(this.store, Roo.data);
9171 // this.store.on('load', this.onLoad, this);
9173 // this.store.load();
9177 // onLoad: function ()
9179 // this.fireEvent('load', this);
9189 * Ext JS Library 1.1.1
9190 * Copyright(c) 2006-2007, Ext JS, LLC.
9192 * Originally Released Under LGPL - original licence link has changed is not relivant.
9195 * <script type="text/javascript">
9198 // as we use this in bootstrap.
9199 Roo.namespace('Roo.form');
9201 * @class Roo.form.Action
9202 * Internal Class used to handle form actions
9204 * @param {Roo.form.BasicForm} el The form element or its id
9205 * @param {Object} config Configuration options
9210 // define the action interface
9211 Roo.form.Action = function(form, options){
9213 this.options = options || {};
9216 * Client Validation Failed
9219 Roo.form.Action.CLIENT_INVALID = 'client';
9221 * Server Validation Failed
9224 Roo.form.Action.SERVER_INVALID = 'server';
9226 * Connect to Server Failed
9229 Roo.form.Action.CONNECT_FAILURE = 'connect';
9231 * Reading Data from Server Failed
9234 Roo.form.Action.LOAD_FAILURE = 'load';
9236 Roo.form.Action.prototype = {
9238 failureType : undefined,
9239 response : undefined,
9243 run : function(options){
9248 success : function(response){
9253 handleResponse : function(response){
9257 // default connection failure
9258 failure : function(response){
9260 this.response = response;
9261 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9262 this.form.afterAction(this, false);
9265 processResponse : function(response){
9266 this.response = response;
9267 if(!response.responseText){
9270 this.result = this.handleResponse(response);
9274 // utility functions used internally
9275 getUrl : function(appendParams){
9276 var url = this.options.url || this.form.url || this.form.el.dom.action;
9278 var p = this.getParams();
9280 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9286 getMethod : function(){
9287 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9290 getParams : function(){
9291 var bp = this.form.baseParams;
9292 var p = this.options.params;
9294 if(typeof p == "object"){
9295 p = Roo.urlEncode(Roo.applyIf(p, bp));
9296 }else if(typeof p == 'string' && bp){
9297 p += '&' + Roo.urlEncode(bp);
9300 p = Roo.urlEncode(bp);
9305 createCallback : function(){
9307 success: this.success,
9308 failure: this.failure,
9310 timeout: (this.form.timeout*1000),
9311 upload: this.form.fileUpload ? this.success : undefined
9316 Roo.form.Action.Submit = function(form, options){
9317 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9320 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9323 haveProgress : false,
9324 uploadComplete : false,
9326 // uploadProgress indicator.
9327 uploadProgress : function()
9329 if (!this.form.progressUrl) {
9333 if (!this.haveProgress) {
9334 Roo.MessageBox.progress("Uploading", "Uploading");
9336 if (this.uploadComplete) {
9337 Roo.MessageBox.hide();
9341 this.haveProgress = true;
9343 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9345 var c = new Roo.data.Connection();
9347 url : this.form.progressUrl,
9352 success : function(req){
9353 //console.log(data);
9357 rdata = Roo.decode(req.responseText)
9359 Roo.log("Invalid data from server..");
9363 if (!rdata || !rdata.success) {
9365 Roo.MessageBox.alert(Roo.encode(rdata));
9368 var data = rdata.data;
9370 if (this.uploadComplete) {
9371 Roo.MessageBox.hide();
9376 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9377 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9380 this.uploadProgress.defer(2000,this);
9383 failure: function(data) {
9384 Roo.log('progress url failed ');
9395 // run get Values on the form, so it syncs any secondary forms.
9396 this.form.getValues();
9398 var o = this.options;
9399 var method = this.getMethod();
9400 var isPost = method == 'POST';
9401 if(o.clientValidation === false || this.form.isValid()){
9403 if (this.form.progressUrl) {
9404 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9405 (new Date() * 1) + '' + Math.random());
9410 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9411 form:this.form.el.dom,
9412 url:this.getUrl(!isPost),
9414 params:isPost ? this.getParams() : null,
9415 isUpload: this.form.fileUpload,
9416 formData : this.form.formData
9419 this.uploadProgress();
9421 }else if (o.clientValidation !== false){ // client validation failed
9422 this.failureType = Roo.form.Action.CLIENT_INVALID;
9423 this.form.afterAction(this, false);
9427 success : function(response)
9429 this.uploadComplete= true;
9430 if (this.haveProgress) {
9431 Roo.MessageBox.hide();
9435 var result = this.processResponse(response);
9436 if(result === true || result.success){
9437 this.form.afterAction(this, true);
9441 this.form.markInvalid(result.errors);
9442 this.failureType = Roo.form.Action.SERVER_INVALID;
9444 this.form.afterAction(this, false);
9446 failure : function(response)
9448 this.uploadComplete= true;
9449 if (this.haveProgress) {
9450 Roo.MessageBox.hide();
9453 this.response = response;
9454 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9455 this.form.afterAction(this, false);
9458 handleResponse : function(response){
9459 if(this.form.errorReader){
9460 var rs = this.form.errorReader.read(response);
9463 for(var i = 0, len = rs.records.length; i < len; i++) {
9464 var r = rs.records[i];
9468 if(errors.length < 1){
9472 success : rs.success,
9478 ret = Roo.decode(response.responseText);
9482 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9492 Roo.form.Action.Load = function(form, options){
9493 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9494 this.reader = this.form.reader;
9497 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9502 Roo.Ajax.request(Roo.apply(
9503 this.createCallback(), {
9504 method:this.getMethod(),
9505 url:this.getUrl(false),
9506 params:this.getParams()
9510 success : function(response){
9512 var result = this.processResponse(response);
9513 if(result === true || !result.success || !result.data){
9514 this.failureType = Roo.form.Action.LOAD_FAILURE;
9515 this.form.afterAction(this, false);
9518 this.form.clearInvalid();
9519 this.form.setValues(result.data);
9520 this.form.afterAction(this, true);
9523 handleResponse : function(response){
9524 if(this.form.reader){
9525 var rs = this.form.reader.read(response);
9526 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9528 success : rs.success,
9532 return Roo.decode(response.responseText);
9536 Roo.form.Action.ACTION_TYPES = {
9537 'load' : Roo.form.Action.Load,
9538 'submit' : Roo.form.Action.Submit
9547 * @class Roo.bootstrap.Form
9548 * @extends Roo.bootstrap.Component
9549 * Bootstrap Form class
9550 * @cfg {String} method GET | POST (default POST)
9551 * @cfg {String} labelAlign top | left (default top)
9552 * @cfg {String} align left | right - for navbars
9553 * @cfg {Boolean} loadMask load mask when submit (default true)
9558 * @param {Object} config The config object
9562 Roo.bootstrap.Form = function(config){
9564 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9566 Roo.bootstrap.Form.popover.apply();
9570 * @event clientvalidation
9571 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9572 * @param {Form} this
9573 * @param {Boolean} valid true if the form has passed client-side validation
9575 clientvalidation: true,
9577 * @event beforeaction
9578 * Fires before any action is performed. Return false to cancel the action.
9579 * @param {Form} this
9580 * @param {Action} action The action to be performed
9584 * @event actionfailed
9585 * Fires when an action fails.
9586 * @param {Form} this
9587 * @param {Action} action The action that failed
9589 actionfailed : true,
9591 * @event actioncomplete
9592 * Fires when an action is completed.
9593 * @param {Form} this
9594 * @param {Action} action The action that completed
9596 actioncomplete : true
9600 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9603 * @cfg {String} method
9604 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9609 * The URL to use for form actions if one isn't supplied in the action options.
9612 * @cfg {Boolean} fileUpload
9613 * Set to true if this form is a file upload.
9617 * @cfg {Object} baseParams
9618 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9622 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9626 * @cfg {Sting} align (left|right) for navbar forms
9631 activeAction : null,
9634 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9635 * element by passing it or its id or mask the form itself by passing in true.
9638 waitMsgTarget : false,
9643 * @cfg {Boolean} errorMask (true|false) default false
9648 * @cfg {Number} maskOffset Default 100
9653 * @cfg {Boolean} maskBody
9657 getAutoCreate : function(){
9661 method : this.method || 'POST',
9662 id : this.id || Roo.id(),
9665 if (this.parent().xtype.match(/^Nav/)) {
9666 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9670 if (this.labelAlign == 'left' ) {
9671 cfg.cls += ' form-horizontal';
9677 initEvents : function()
9679 this.el.on('submit', this.onSubmit, this);
9680 // this was added as random key presses on the form where triggering form submit.
9681 this.el.on('keypress', function(e) {
9682 if (e.getCharCode() != 13) {
9685 // we might need to allow it for textareas.. and some other items.
9686 // check e.getTarget().
9688 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9692 Roo.log("keypress blocked");
9700 onSubmit : function(e){
9705 * Returns true if client-side validation on the form is successful.
9708 isValid : function(){
9709 var items = this.getItems();
9713 items.each(function(f){
9719 Roo.log('invalid field: ' + f.name);
9723 if(!target && f.el.isVisible(true)){
9729 if(this.errorMask && !valid){
9730 Roo.bootstrap.Form.popover.mask(this, target);
9737 * Returns true if any fields in this form have changed since their original load.
9740 isDirty : function(){
9742 var items = this.getItems();
9743 items.each(function(f){
9753 * Performs a predefined action (submit or load) or custom actions you define on this form.
9754 * @param {String} actionName The name of the action type
9755 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9756 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9757 * accept other config options):
9759 Property Type Description
9760 ---------------- --------------- ----------------------------------------------------------------------------------
9761 url String The url for the action (defaults to the form's url)
9762 method String The form method to use (defaults to the form's method, or POST if not defined)
9763 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9764 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9765 validate the form on the client (defaults to false)
9767 * @return {BasicForm} this
9769 doAction : function(action, options){
9770 if(typeof action == 'string'){
9771 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9773 if(this.fireEvent('beforeaction', this, action) !== false){
9774 this.beforeAction(action);
9775 action.run.defer(100, action);
9781 beforeAction : function(action){
9782 var o = action.options;
9787 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9789 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9792 // not really supported yet.. ??
9794 //if(this.waitMsgTarget === true){
9795 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9796 //}else if(this.waitMsgTarget){
9797 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9798 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9800 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9806 afterAction : function(action, success){
9807 this.activeAction = null;
9808 var o = action.options;
9813 Roo.get(document.body).unmask();
9819 //if(this.waitMsgTarget === true){
9820 // this.el.unmask();
9821 //}else if(this.waitMsgTarget){
9822 // this.waitMsgTarget.unmask();
9824 // Roo.MessageBox.updateProgress(1);
9825 // Roo.MessageBox.hide();
9832 Roo.callback(o.success, o.scope, [this, action]);
9833 this.fireEvent('actioncomplete', this, action);
9837 // failure condition..
9838 // we have a scenario where updates need confirming.
9839 // eg. if a locking scenario exists..
9840 // we look for { errors : { needs_confirm : true }} in the response.
9842 (typeof(action.result) != 'undefined') &&
9843 (typeof(action.result.errors) != 'undefined') &&
9844 (typeof(action.result.errors.needs_confirm) != 'undefined')
9847 Roo.log("not supported yet");
9850 Roo.MessageBox.confirm(
9851 "Change requires confirmation",
9852 action.result.errorMsg,
9857 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9867 Roo.callback(o.failure, o.scope, [this, action]);
9868 // show an error message if no failed handler is set..
9869 if (!this.hasListener('actionfailed')) {
9870 Roo.log("need to add dialog support");
9872 Roo.MessageBox.alert("Error",
9873 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9874 action.result.errorMsg :
9875 "Saving Failed, please check your entries or try again"
9880 this.fireEvent('actionfailed', this, action);
9885 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9886 * @param {String} id The value to search for
9889 findField : function(id){
9890 var items = this.getItems();
9891 var field = items.get(id);
9893 items.each(function(f){
9894 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9901 return field || null;
9904 * Mark fields in this form invalid in bulk.
9905 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9906 * @return {BasicForm} this
9908 markInvalid : function(errors){
9909 if(errors instanceof Array){
9910 for(var i = 0, len = errors.length; i < len; i++){
9911 var fieldError = errors[i];
9912 var f = this.findField(fieldError.id);
9914 f.markInvalid(fieldError.msg);
9920 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9921 field.markInvalid(errors[id]);
9925 //Roo.each(this.childForms || [], function (f) {
9926 // f.markInvalid(errors);
9933 * Set values for fields in this form in bulk.
9934 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9935 * @return {BasicForm} this
9937 setValues : function(values){
9938 if(values instanceof Array){ // array of objects
9939 for(var i = 0, len = values.length; i < len; i++){
9941 var f = this.findField(v.id);
9943 f.setValue(v.value);
9944 if(this.trackResetOnLoad){
9945 f.originalValue = f.getValue();
9949 }else{ // object hash
9952 if(typeof values[id] != 'function' && (field = this.findField(id))){
9954 if (field.setFromData &&
9956 field.displayField &&
9957 // combos' with local stores can
9958 // be queried via setValue()
9959 // to set their value..
9960 (field.store && !field.store.isLocal)
9964 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9965 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9966 field.setFromData(sd);
9968 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9970 field.setFromData(values);
9973 field.setValue(values[id]);
9977 if(this.trackResetOnLoad){
9978 field.originalValue = field.getValue();
9984 //Roo.each(this.childForms || [], function (f) {
9985 // f.setValues(values);
9992 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9993 * they are returned as an array.
9994 * @param {Boolean} asString
9997 getValues : function(asString){
9998 //if (this.childForms) {
9999 // copy values from the child forms
10000 // Roo.each(this.childForms, function (f) {
10001 // this.setValues(f.getValues());
10007 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10008 if(asString === true){
10011 return Roo.urlDecode(fs);
10015 * Returns the fields in this form as an object with key/value pairs.
10016 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10019 getFieldValues : function(with_hidden)
10021 var items = this.getItems();
10023 items.each(function(f){
10025 if (!f.getName()) {
10029 var v = f.getValue();
10031 if (f.inputType =='radio') {
10032 if (typeof(ret[f.getName()]) == 'undefined') {
10033 ret[f.getName()] = ''; // empty..
10036 if (!f.el.dom.checked) {
10040 v = f.el.dom.value;
10044 if(f.xtype == 'MoneyField'){
10045 ret[f.currencyName] = f.getCurrency();
10048 // not sure if this supported any more..
10049 if ((typeof(v) == 'object') && f.getRawValue) {
10050 v = f.getRawValue() ; // dates..
10052 // combo boxes where name != hiddenName...
10053 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10054 ret[f.name] = f.getRawValue();
10056 ret[f.getName()] = v;
10063 * Clears all invalid messages in this form.
10064 * @return {BasicForm} this
10066 clearInvalid : function(){
10067 var items = this.getItems();
10069 items.each(function(f){
10077 * Resets this form.
10078 * @return {BasicForm} this
10080 reset : function(){
10081 var items = this.getItems();
10082 items.each(function(f){
10086 Roo.each(this.childForms || [], function (f) {
10094 getItems : function()
10096 var r=new Roo.util.MixedCollection(false, function(o){
10097 return o.id || (o.id = Roo.id());
10099 var iter = function(el) {
10106 Roo.each(el.items,function(e) {
10115 hideFields : function(items)
10117 Roo.each(items, function(i){
10119 var f = this.findField(i);
10130 showFields : function(items)
10132 Roo.each(items, function(i){
10134 var f = this.findField(i);
10147 Roo.apply(Roo.bootstrap.Form, {
10163 intervalID : false,
10169 if(this.isApplied){
10174 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10175 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10176 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10177 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10180 this.maskEl.top.enableDisplayMode("block");
10181 this.maskEl.left.enableDisplayMode("block");
10182 this.maskEl.bottom.enableDisplayMode("block");
10183 this.maskEl.right.enableDisplayMode("block");
10185 this.toolTip = new Roo.bootstrap.Tooltip({
10186 cls : 'roo-form-error-popover',
10188 'left' : ['r-l', [-2,0], 'right'],
10189 'right' : ['l-r', [2,0], 'left'],
10190 'bottom' : ['tl-bl', [0,2], 'top'],
10191 'top' : [ 'bl-tl', [0,-2], 'bottom']
10195 this.toolTip.render(Roo.get(document.body));
10197 this.toolTip.el.enableDisplayMode("block");
10199 Roo.get(document.body).on('click', function(){
10203 Roo.get(document.body).on('touchstart', function(){
10207 this.isApplied = true
10210 mask : function(form, target)
10214 this.target = target;
10216 if(!this.form.errorMask || !target.el){
10220 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10222 Roo.log(scrollable);
10224 var ot = this.target.el.calcOffsetsTo(scrollable);
10226 var scrollTo = ot[1] - this.form.maskOffset;
10228 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10230 scrollable.scrollTo('top', scrollTo);
10232 var box = this.target.el.getBox();
10234 var zIndex = Roo.bootstrap.Modal.zIndex++;
10237 this.maskEl.top.setStyle('position', 'absolute');
10238 this.maskEl.top.setStyle('z-index', zIndex);
10239 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10240 this.maskEl.top.setLeft(0);
10241 this.maskEl.top.setTop(0);
10242 this.maskEl.top.show();
10244 this.maskEl.left.setStyle('position', 'absolute');
10245 this.maskEl.left.setStyle('z-index', zIndex);
10246 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10247 this.maskEl.left.setLeft(0);
10248 this.maskEl.left.setTop(box.y - this.padding);
10249 this.maskEl.left.show();
10251 this.maskEl.bottom.setStyle('position', 'absolute');
10252 this.maskEl.bottom.setStyle('z-index', zIndex);
10253 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10254 this.maskEl.bottom.setLeft(0);
10255 this.maskEl.bottom.setTop(box.bottom + this.padding);
10256 this.maskEl.bottom.show();
10258 this.maskEl.right.setStyle('position', 'absolute');
10259 this.maskEl.right.setStyle('z-index', zIndex);
10260 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10261 this.maskEl.right.setLeft(box.right + this.padding);
10262 this.maskEl.right.setTop(box.y - this.padding);
10263 this.maskEl.right.show();
10265 this.toolTip.bindEl = this.target.el;
10267 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10269 var tip = this.target.blankText;
10271 if(this.target.getValue() !== '' ) {
10273 if (this.target.invalidText.length) {
10274 tip = this.target.invalidText;
10275 } else if (this.target.regexText.length){
10276 tip = this.target.regexText;
10280 this.toolTip.show(tip);
10282 this.intervalID = window.setInterval(function() {
10283 Roo.bootstrap.Form.popover.unmask();
10286 window.onwheel = function(){ return false;};
10288 (function(){ this.isMasked = true; }).defer(500, this);
10292 unmask : function()
10294 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10298 this.maskEl.top.setStyle('position', 'absolute');
10299 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10300 this.maskEl.top.hide();
10302 this.maskEl.left.setStyle('position', 'absolute');
10303 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10304 this.maskEl.left.hide();
10306 this.maskEl.bottom.setStyle('position', 'absolute');
10307 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10308 this.maskEl.bottom.hide();
10310 this.maskEl.right.setStyle('position', 'absolute');
10311 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10312 this.maskEl.right.hide();
10314 this.toolTip.hide();
10316 this.toolTip.el.hide();
10318 window.onwheel = function(){ return true;};
10320 if(this.intervalID){
10321 window.clearInterval(this.intervalID);
10322 this.intervalID = false;
10325 this.isMasked = false;
10335 * Ext JS Library 1.1.1
10336 * Copyright(c) 2006-2007, Ext JS, LLC.
10338 * Originally Released Under LGPL - original licence link has changed is not relivant.
10341 * <script type="text/javascript">
10344 * @class Roo.form.VTypes
10345 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10348 Roo.form.VTypes = function(){
10349 // closure these in so they are only created once.
10350 var alpha = /^[a-zA-Z_]+$/;
10351 var alphanum = /^[a-zA-Z0-9_]+$/;
10352 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10353 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10355 // All these messages and functions are configurable
10358 * The function used to validate email addresses
10359 * @param {String} value The email address
10361 'email' : function(v){
10362 return email.test(v);
10365 * The error text to display when the email validation function returns false
10368 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10370 * The keystroke filter mask to be applied on email input
10373 'emailMask' : /[a-z0-9_\.\-@]/i,
10376 * The function used to validate URLs
10377 * @param {String} value The URL
10379 'url' : function(v){
10380 return url.test(v);
10383 * The error text to display when the url validation function returns false
10386 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10389 * The function used to validate alpha values
10390 * @param {String} value The value
10392 'alpha' : function(v){
10393 return alpha.test(v);
10396 * The error text to display when the alpha validation function returns false
10399 'alphaText' : 'This field should only contain letters and _',
10401 * The keystroke filter mask to be applied on alpha input
10404 'alphaMask' : /[a-z_]/i,
10407 * The function used to validate alphanumeric values
10408 * @param {String} value The value
10410 'alphanum' : function(v){
10411 return alphanum.test(v);
10414 * The error text to display when the alphanumeric validation function returns false
10417 'alphanumText' : 'This field should only contain letters, numbers and _',
10419 * The keystroke filter mask to be applied on alphanumeric input
10422 'alphanumMask' : /[a-z0-9_]/i
10432 * @class Roo.bootstrap.Input
10433 * @extends Roo.bootstrap.Component
10434 * Bootstrap Input class
10435 * @cfg {Boolean} disabled is it disabled
10436 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10437 * @cfg {String} name name of the input
10438 * @cfg {string} fieldLabel - the label associated
10439 * @cfg {string} placeholder - placeholder to put in text.
10440 * @cfg {string} before - input group add on before
10441 * @cfg {string} after - input group add on after
10442 * @cfg {string} size - (lg|sm) or leave empty..
10443 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10444 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10445 * @cfg {Number} md colspan out of 12 for computer-sized screens
10446 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10447 * @cfg {string} value default value of the input
10448 * @cfg {Number} labelWidth set the width of label
10449 * @cfg {Number} labellg set the width of label (1-12)
10450 * @cfg {Number} labelmd set the width of label (1-12)
10451 * @cfg {Number} labelsm set the width of label (1-12)
10452 * @cfg {Number} labelxs set the width of label (1-12)
10453 * @cfg {String} labelAlign (top|left)
10454 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10455 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10456 * @cfg {String} indicatorpos (left|right) default left
10457 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10458 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10459 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10461 * @cfg {String} align (left|center|right) Default left
10462 * @cfg {Boolean} forceFeedback (true|false) Default false
10465 * Create a new Input
10466 * @param {Object} config The config object
10469 Roo.bootstrap.Input = function(config){
10471 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10476 * Fires when this field receives input focus.
10477 * @param {Roo.form.Field} this
10482 * Fires when this field loses input focus.
10483 * @param {Roo.form.Field} this
10487 * @event specialkey
10488 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10489 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10490 * @param {Roo.form.Field} this
10491 * @param {Roo.EventObject} e The event object
10496 * Fires just before the field blurs if the field value has changed.
10497 * @param {Roo.form.Field} this
10498 * @param {Mixed} newValue The new value
10499 * @param {Mixed} oldValue The original value
10504 * Fires after the field has been marked as invalid.
10505 * @param {Roo.form.Field} this
10506 * @param {String} msg The validation message
10511 * Fires after the field has been validated with no errors.
10512 * @param {Roo.form.Field} this
10517 * Fires after the key up
10518 * @param {Roo.form.Field} this
10519 * @param {Roo.EventObject} e The event Object
10524 * Fires after the user pastes into input
10525 * @param {Roo.form.Field} this
10526 * @param {Roo.EventObject} e The event Object
10532 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10534 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10535 automatic validation (defaults to "keyup").
10537 validationEvent : "keyup",
10539 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10541 validateOnBlur : true,
10543 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10545 validationDelay : 250,
10547 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10549 focusClass : "x-form-focus", // not needed???
10553 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10555 invalidClass : "has-warning",
10558 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10560 validClass : "has-success",
10563 * @cfg {Boolean} hasFeedback (true|false) default true
10565 hasFeedback : true,
10568 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10570 invalidFeedbackClass : "glyphicon-warning-sign",
10573 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10575 validFeedbackClass : "glyphicon-ok",
10578 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10580 selectOnFocus : false,
10583 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10587 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10592 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10594 disableKeyFilter : false,
10597 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10601 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10605 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10607 blankText : "Please complete this mandatory field",
10610 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10614 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10616 maxLength : Number.MAX_VALUE,
10618 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10620 minLengthText : "The minimum length for this field is {0}",
10622 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10624 maxLengthText : "The maximum length for this field is {0}",
10628 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10629 * If available, this function will be called only after the basic validators all return true, and will be passed the
10630 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10634 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10635 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10636 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10640 * @cfg {String} regexText -- Depricated - use Invalid Text
10645 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10651 autocomplete: false,
10655 inputType : 'text',
10658 placeholder: false,
10663 preventMark: false,
10664 isFormField : true,
10667 labelAlign : false,
10670 formatedValue : false,
10671 forceFeedback : false,
10673 indicatorpos : 'left',
10683 parentLabelAlign : function()
10686 while (parent.parent()) {
10687 parent = parent.parent();
10688 if (typeof(parent.labelAlign) !='undefined') {
10689 return parent.labelAlign;
10696 getAutoCreate : function()
10698 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10704 if(this.inputType != 'hidden'){
10705 cfg.cls = 'form-group' //input-group
10711 type : this.inputType,
10712 value : this.value,
10713 cls : 'form-control',
10714 placeholder : this.placeholder || '',
10715 autocomplete : this.autocomplete || 'new-password'
10717 if (this.inputType == 'file') {
10718 input.style = 'overflow:hidden'; // why not in CSS?
10721 if(this.capture.length){
10722 input.capture = this.capture;
10725 if(this.accept.length){
10726 input.accept = this.accept + "/*";
10730 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10733 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10734 input.maxLength = this.maxLength;
10737 if (this.disabled) {
10738 input.disabled=true;
10741 if (this.readOnly) {
10742 input.readonly=true;
10746 input.name = this.name;
10750 input.cls += ' input-' + this.size;
10754 ['xs','sm','md','lg'].map(function(size){
10755 if (settings[size]) {
10756 cfg.cls += ' col-' + size + '-' + settings[size];
10760 var inputblock = input;
10764 cls: 'glyphicon form-control-feedback'
10767 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10770 cls : 'has-feedback',
10778 if (this.before || this.after) {
10781 cls : 'input-group',
10785 if (this.before && typeof(this.before) == 'string') {
10787 inputblock.cn.push({
10789 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10793 if (this.before && typeof(this.before) == 'object') {
10794 this.before = Roo.factory(this.before);
10796 inputblock.cn.push({
10798 cls : 'roo-input-before input-group-prepend input-group-' +
10799 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10803 inputblock.cn.push(input);
10805 if (this.after && typeof(this.after) == 'string') {
10806 inputblock.cn.push({
10808 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10812 if (this.after && typeof(this.after) == 'object') {
10813 this.after = Roo.factory(this.after);
10815 inputblock.cn.push({
10817 cls : 'roo-input-after input-group-append input-group-' +
10818 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10822 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10823 inputblock.cls += ' has-feedback';
10824 inputblock.cn.push(feedback);
10829 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10830 tooltip : 'This field is required'
10832 if (this.allowBlank ) {
10833 indicator.style = this.allowBlank ? ' display:none' : '';
10835 if (align ==='left' && this.fieldLabel.length) {
10837 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10844 cls : 'control-label col-form-label',
10845 html : this.fieldLabel
10856 var labelCfg = cfg.cn[1];
10857 var contentCfg = cfg.cn[2];
10859 if(this.indicatorpos == 'right'){
10864 cls : 'control-label col-form-label',
10868 html : this.fieldLabel
10882 labelCfg = cfg.cn[0];
10883 contentCfg = cfg.cn[1];
10887 if(this.labelWidth > 12){
10888 labelCfg.style = "width: " + this.labelWidth + 'px';
10891 if(this.labelWidth < 13 && this.labelmd == 0){
10892 this.labelmd = this.labelWidth;
10895 if(this.labellg > 0){
10896 labelCfg.cls += ' col-lg-' + this.labellg;
10897 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10900 if(this.labelmd > 0){
10901 labelCfg.cls += ' col-md-' + this.labelmd;
10902 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10905 if(this.labelsm > 0){
10906 labelCfg.cls += ' col-sm-' + this.labelsm;
10907 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10910 if(this.labelxs > 0){
10911 labelCfg.cls += ' col-xs-' + this.labelxs;
10912 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10916 } else if ( this.fieldLabel.length) {
10923 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10924 tooltip : 'This field is required',
10925 style : this.allowBlank ? ' display:none' : ''
10929 //cls : 'input-group-addon',
10930 html : this.fieldLabel
10938 if(this.indicatorpos == 'right'){
10943 //cls : 'input-group-addon',
10944 html : this.fieldLabel
10949 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10950 tooltip : 'This field is required',
10951 style : this.allowBlank ? ' display:none' : ''
10971 if (this.parentType === 'Navbar' && this.parent().bar) {
10972 cfg.cls += ' navbar-form';
10975 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10976 // on BS4 we do this only if not form
10977 cfg.cls += ' navbar-form';
10985 * return the real input element.
10987 inputEl: function ()
10989 return this.el.select('input.form-control',true).first();
10992 tooltipEl : function()
10994 return this.inputEl();
10997 indicatorEl : function()
10999 if (Roo.bootstrap.version == 4) {
11000 return false; // not enabled in v4 yet.
11003 var indicator = this.el.select('i.roo-required-indicator',true).first();
11013 setDisabled : function(v)
11015 var i = this.inputEl().dom;
11017 i.removeAttribute('disabled');
11021 i.setAttribute('disabled','true');
11023 initEvents : function()
11026 this.inputEl().on("keydown" , this.fireKey, this);
11027 this.inputEl().on("focus", this.onFocus, this);
11028 this.inputEl().on("blur", this.onBlur, this);
11030 this.inputEl().relayEvent('keyup', this);
11031 this.inputEl().relayEvent('paste', this);
11033 this.indicator = this.indicatorEl();
11035 if(this.indicator){
11036 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11039 // reference to original value for reset
11040 this.originalValue = this.getValue();
11041 //Roo.form.TextField.superclass.initEvents.call(this);
11042 if(this.validationEvent == 'keyup'){
11043 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11044 this.inputEl().on('keyup', this.filterValidation, this);
11046 else if(this.validationEvent !== false){
11047 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11050 if(this.selectOnFocus){
11051 this.on("focus", this.preFocus, this);
11054 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11055 this.inputEl().on("keypress", this.filterKeys, this);
11057 this.inputEl().relayEvent('keypress', this);
11060 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11061 this.el.on("click", this.autoSize, this);
11064 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11065 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11068 if (typeof(this.before) == 'object') {
11069 this.before.render(this.el.select('.roo-input-before',true).first());
11071 if (typeof(this.after) == 'object') {
11072 this.after.render(this.el.select('.roo-input-after',true).first());
11075 this.inputEl().on('change', this.onChange, this);
11078 filterValidation : function(e){
11079 if(!e.isNavKeyPress()){
11080 this.validationTask.delay(this.validationDelay);
11084 * Validates the field value
11085 * @return {Boolean} True if the value is valid, else false
11087 validate : function(){
11088 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11089 if(this.disabled || this.validateValue(this.getRawValue())){
11094 this.markInvalid();
11100 * Validates a value according to the field's validation rules and marks the field as invalid
11101 * if the validation fails
11102 * @param {Mixed} value The value to validate
11103 * @return {Boolean} True if the value is valid, else false
11105 validateValue : function(value)
11107 if(this.getVisibilityEl().hasClass('hidden')){
11111 if(value.length < 1) { // if it's blank
11112 if(this.allowBlank){
11118 if(value.length < this.minLength){
11121 if(value.length > this.maxLength){
11125 var vt = Roo.form.VTypes;
11126 if(!vt[this.vtype](value, this)){
11130 if(typeof this.validator == "function"){
11131 var msg = this.validator(value);
11135 if (typeof(msg) == 'string') {
11136 this.invalidText = msg;
11140 if(this.regex && !this.regex.test(value)){
11148 fireKey : function(e){
11149 //Roo.log('field ' + e.getKey());
11150 if(e.isNavKeyPress()){
11151 this.fireEvent("specialkey", this, e);
11154 focus : function (selectText){
11156 this.inputEl().focus();
11157 if(selectText === true){
11158 this.inputEl().dom.select();
11164 onFocus : function(){
11165 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11166 // this.el.addClass(this.focusClass);
11168 if(!this.hasFocus){
11169 this.hasFocus = true;
11170 this.startValue = this.getValue();
11171 this.fireEvent("focus", this);
11175 beforeBlur : Roo.emptyFn,
11179 onBlur : function(){
11181 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11182 //this.el.removeClass(this.focusClass);
11184 this.hasFocus = false;
11185 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11188 var v = this.getValue();
11189 if(String(v) !== String(this.startValue)){
11190 this.fireEvent('change', this, v, this.startValue);
11192 this.fireEvent("blur", this);
11195 onChange : function(e)
11197 var v = this.getValue();
11198 if(String(v) !== String(this.startValue)){
11199 this.fireEvent('change', this, v, this.startValue);
11205 * Resets the current field value to the originally loaded value and clears any validation messages
11207 reset : function(){
11208 this.setValue(this.originalValue);
11212 * Returns the name of the field
11213 * @return {Mixed} name The name field
11215 getName: function(){
11219 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11220 * @return {Mixed} value The field value
11222 getValue : function(){
11224 var v = this.inputEl().getValue();
11229 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11230 * @return {Mixed} value The field value
11232 getRawValue : function(){
11233 var v = this.inputEl().getValue();
11239 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11240 * @param {Mixed} value The value to set
11242 setRawValue : function(v){
11243 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11246 selectText : function(start, end){
11247 var v = this.getRawValue();
11249 start = start === undefined ? 0 : start;
11250 end = end === undefined ? v.length : end;
11251 var d = this.inputEl().dom;
11252 if(d.setSelectionRange){
11253 d.setSelectionRange(start, end);
11254 }else if(d.createTextRange){
11255 var range = d.createTextRange();
11256 range.moveStart("character", start);
11257 range.moveEnd("character", v.length-end);
11264 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11265 * @param {Mixed} value The value to set
11267 setValue : function(v){
11270 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11276 processValue : function(value){
11277 if(this.stripCharsRe){
11278 var newValue = value.replace(this.stripCharsRe, '');
11279 if(newValue !== value){
11280 this.setRawValue(newValue);
11287 preFocus : function(){
11289 if(this.selectOnFocus){
11290 this.inputEl().dom.select();
11293 filterKeys : function(e){
11294 var k = e.getKey();
11295 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11298 var c = e.getCharCode(), cc = String.fromCharCode(c);
11299 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11302 if(!this.maskRe.test(cc)){
11307 * Clear any invalid styles/messages for this field
11309 clearInvalid : function(){
11311 if(!this.el || this.preventMark){ // not rendered
11316 this.el.removeClass([this.invalidClass, 'is-invalid']);
11318 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11320 var feedback = this.el.select('.form-control-feedback', true).first();
11323 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11328 if(this.indicator){
11329 this.indicator.removeClass('visible');
11330 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11333 this.fireEvent('valid', this);
11337 * Mark this field as valid
11339 markValid : function()
11341 if(!this.el || this.preventMark){ // not rendered...
11345 this.el.removeClass([this.invalidClass, this.validClass]);
11346 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11348 var feedback = this.el.select('.form-control-feedback', true).first();
11351 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11354 if(this.indicator){
11355 this.indicator.removeClass('visible');
11356 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11364 if(this.allowBlank && !this.getRawValue().length){
11367 if (Roo.bootstrap.version == 3) {
11368 this.el.addClass(this.validClass);
11370 this.inputEl().addClass('is-valid');
11373 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11375 var feedback = this.el.select('.form-control-feedback', true).first();
11378 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11379 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11384 this.fireEvent('valid', this);
11388 * Mark this field as invalid
11389 * @param {String} msg The validation message
11391 markInvalid : function(msg)
11393 if(!this.el || this.preventMark){ // not rendered
11397 this.el.removeClass([this.invalidClass, this.validClass]);
11398 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11400 var feedback = this.el.select('.form-control-feedback', true).first();
11403 this.el.select('.form-control-feedback', true).first().removeClass(
11404 [this.invalidFeedbackClass, this.validFeedbackClass]);
11411 if(this.allowBlank && !this.getRawValue().length){
11415 if(this.indicator){
11416 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11417 this.indicator.addClass('visible');
11419 if (Roo.bootstrap.version == 3) {
11420 this.el.addClass(this.invalidClass);
11422 this.inputEl().addClass('is-invalid');
11427 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11429 var feedback = this.el.select('.form-control-feedback', true).first();
11432 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11434 if(this.getValue().length || this.forceFeedback){
11435 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11442 this.fireEvent('invalid', this, msg);
11445 SafariOnKeyDown : function(event)
11447 // this is a workaround for a password hang bug on chrome/ webkit.
11448 if (this.inputEl().dom.type != 'password') {
11452 var isSelectAll = false;
11454 if(this.inputEl().dom.selectionEnd > 0){
11455 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11457 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11458 event.preventDefault();
11463 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11465 event.preventDefault();
11466 // this is very hacky as keydown always get's upper case.
11468 var cc = String.fromCharCode(event.getCharCode());
11469 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11473 adjustWidth : function(tag, w){
11474 tag = tag.toLowerCase();
11475 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11476 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11477 if(tag == 'input'){
11480 if(tag == 'textarea'){
11483 }else if(Roo.isOpera){
11484 if(tag == 'input'){
11487 if(tag == 'textarea'){
11495 setFieldLabel : function(v)
11497 if(!this.rendered){
11501 if(this.indicatorEl()){
11502 var ar = this.el.select('label > span',true);
11504 if (ar.elements.length) {
11505 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11506 this.fieldLabel = v;
11510 var br = this.el.select('label',true);
11512 if(br.elements.length) {
11513 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11514 this.fieldLabel = v;
11518 Roo.log('Cannot Found any of label > span || label in input');
11522 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11523 this.fieldLabel = v;
11538 * @class Roo.bootstrap.TextArea
11539 * @extends Roo.bootstrap.Input
11540 * Bootstrap TextArea class
11541 * @cfg {Number} cols Specifies the visible width of a text area
11542 * @cfg {Number} rows Specifies the visible number of lines in a text area
11543 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11544 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11545 * @cfg {string} html text
11548 * Create a new TextArea
11549 * @param {Object} config The config object
11552 Roo.bootstrap.TextArea = function(config){
11553 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11557 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11567 getAutoCreate : function(){
11569 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11575 if(this.inputType != 'hidden'){
11576 cfg.cls = 'form-group' //input-group
11584 value : this.value || '',
11585 html: this.html || '',
11586 cls : 'form-control',
11587 placeholder : this.placeholder || ''
11591 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11592 input.maxLength = this.maxLength;
11596 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11600 input.cols = this.cols;
11603 if (this.readOnly) {
11604 input.readonly = true;
11608 input.name = this.name;
11612 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11616 ['xs','sm','md','lg'].map(function(size){
11617 if (settings[size]) {
11618 cfg.cls += ' col-' + size + '-' + settings[size];
11622 var inputblock = input;
11624 if(this.hasFeedback && !this.allowBlank){
11628 cls: 'glyphicon form-control-feedback'
11632 cls : 'has-feedback',
11641 if (this.before || this.after) {
11644 cls : 'input-group',
11648 inputblock.cn.push({
11650 cls : 'input-group-addon',
11655 inputblock.cn.push(input);
11657 if(this.hasFeedback && !this.allowBlank){
11658 inputblock.cls += ' has-feedback';
11659 inputblock.cn.push(feedback);
11663 inputblock.cn.push({
11665 cls : 'input-group-addon',
11672 if (align ==='left' && this.fieldLabel.length) {
11677 cls : 'control-label',
11678 html : this.fieldLabel
11689 if(this.labelWidth > 12){
11690 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11693 if(this.labelWidth < 13 && this.labelmd == 0){
11694 this.labelmd = this.labelWidth;
11697 if(this.labellg > 0){
11698 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11699 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11702 if(this.labelmd > 0){
11703 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11704 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11707 if(this.labelsm > 0){
11708 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11709 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11712 if(this.labelxs > 0){
11713 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11714 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11717 } else if ( this.fieldLabel.length) {
11722 //cls : 'input-group-addon',
11723 html : this.fieldLabel
11741 if (this.disabled) {
11742 input.disabled=true;
11749 * return the real textarea element.
11751 inputEl: function ()
11753 return this.el.select('textarea.form-control',true).first();
11757 * Clear any invalid styles/messages for this field
11759 clearInvalid : function()
11762 if(!this.el || this.preventMark){ // not rendered
11766 var label = this.el.select('label', true).first();
11767 var icon = this.el.select('i.fa-star', true).first();
11772 this.el.removeClass( this.validClass);
11773 this.inputEl().removeClass('is-invalid');
11775 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11777 var feedback = this.el.select('.form-control-feedback', true).first();
11780 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11785 this.fireEvent('valid', this);
11789 * Mark this field as valid
11791 markValid : function()
11793 if(!this.el || this.preventMark){ // not rendered
11797 this.el.removeClass([this.invalidClass, this.validClass]);
11798 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11800 var feedback = this.el.select('.form-control-feedback', true).first();
11803 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11806 if(this.disabled || this.allowBlank){
11810 var label = this.el.select('label', true).first();
11811 var icon = this.el.select('i.fa-star', true).first();
11816 if (Roo.bootstrap.version == 3) {
11817 this.el.addClass(this.validClass);
11819 this.inputEl().addClass('is-valid');
11823 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11825 var feedback = this.el.select('.form-control-feedback', true).first();
11828 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11829 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11834 this.fireEvent('valid', this);
11838 * Mark this field as invalid
11839 * @param {String} msg The validation message
11841 markInvalid : function(msg)
11843 if(!this.el || this.preventMark){ // not rendered
11847 this.el.removeClass([this.invalidClass, this.validClass]);
11848 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11850 var feedback = this.el.select('.form-control-feedback', true).first();
11853 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11856 if(this.disabled || this.allowBlank){
11860 var label = this.el.select('label', true).first();
11861 var icon = this.el.select('i.fa-star', true).first();
11863 if(!this.getValue().length && label && !icon){
11864 this.el.createChild({
11866 cls : 'text-danger fa fa-lg fa-star',
11867 tooltip : 'This field is required',
11868 style : 'margin-right:5px;'
11872 if (Roo.bootstrap.version == 3) {
11873 this.el.addClass(this.invalidClass);
11875 this.inputEl().addClass('is-invalid');
11878 // fixme ... this may be depricated need to test..
11879 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11881 var feedback = this.el.select('.form-control-feedback', true).first();
11884 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11886 if(this.getValue().length || this.forceFeedback){
11887 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11894 this.fireEvent('invalid', this, msg);
11902 * trigger field - base class for combo..
11907 * @class Roo.bootstrap.TriggerField
11908 * @extends Roo.bootstrap.Input
11909 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11910 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11911 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11912 * for which you can provide a custom implementation. For example:
11914 var trigger = new Roo.bootstrap.TriggerField();
11915 trigger.onTriggerClick = myTriggerFn;
11916 trigger.applyTo('my-field');
11919 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11920 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11921 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11922 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11923 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11926 * Create a new TriggerField.
11927 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11928 * to the base TextField)
11930 Roo.bootstrap.TriggerField = function(config){
11931 this.mimicing = false;
11932 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11935 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11937 * @cfg {String} triggerClass A CSS class to apply to the trigger
11940 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11945 * @cfg {Boolean} removable (true|false) special filter default false
11949 /** @cfg {Boolean} grow @hide */
11950 /** @cfg {Number} growMin @hide */
11951 /** @cfg {Number} growMax @hide */
11957 autoSize: Roo.emptyFn,
11961 deferHeight : true,
11964 actionMode : 'wrap',
11969 getAutoCreate : function(){
11971 var align = this.labelAlign || this.parentLabelAlign();
11976 cls: 'form-group' //input-group
11983 type : this.inputType,
11984 cls : 'form-control',
11985 autocomplete: 'new-password',
11986 placeholder : this.placeholder || ''
11990 input.name = this.name;
11993 input.cls += ' input-' + this.size;
11996 if (this.disabled) {
11997 input.disabled=true;
12000 var inputblock = input;
12002 if(this.hasFeedback && !this.allowBlank){
12006 cls: 'glyphicon form-control-feedback'
12009 if(this.removable && !this.editable ){
12011 cls : 'has-feedback',
12017 cls : 'roo-combo-removable-btn close'
12024 cls : 'has-feedback',
12033 if(this.removable && !this.editable ){
12035 cls : 'roo-removable',
12041 cls : 'roo-combo-removable-btn close'
12048 if (this.before || this.after) {
12051 cls : 'input-group',
12055 inputblock.cn.push({
12057 cls : 'input-group-addon input-group-prepend input-group-text',
12062 inputblock.cn.push(input);
12064 if(this.hasFeedback && !this.allowBlank){
12065 inputblock.cls += ' has-feedback';
12066 inputblock.cn.push(feedback);
12070 inputblock.cn.push({
12072 cls : 'input-group-addon input-group-append input-group-text',
12081 var ibwrap = inputblock;
12086 cls: 'roo-select2-choices',
12090 cls: 'roo-select2-search-field',
12102 cls: 'roo-select2-container input-group',
12107 cls: 'form-hidden-field'
12113 if(!this.multiple && this.showToggleBtn){
12119 if (this.caret != false) {
12122 cls: 'fa fa-' + this.caret
12129 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12131 Roo.bootstrap.version == 3 ? caret : '',
12134 cls: 'combobox-clear',
12148 combobox.cls += ' roo-select2-container-multi';
12152 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12153 tooltip : 'This field is required'
12155 if (Roo.bootstrap.version == 4) {
12158 style : 'display:none'
12163 if (align ==='left' && this.fieldLabel.length) {
12165 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12172 cls : 'control-label',
12173 html : this.fieldLabel
12185 var labelCfg = cfg.cn[1];
12186 var contentCfg = cfg.cn[2];
12188 if(this.indicatorpos == 'right'){
12193 cls : 'control-label',
12197 html : this.fieldLabel
12211 labelCfg = cfg.cn[0];
12212 contentCfg = cfg.cn[1];
12215 if(this.labelWidth > 12){
12216 labelCfg.style = "width: " + this.labelWidth + 'px';
12219 if(this.labelWidth < 13 && this.labelmd == 0){
12220 this.labelmd = this.labelWidth;
12223 if(this.labellg > 0){
12224 labelCfg.cls += ' col-lg-' + this.labellg;
12225 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12228 if(this.labelmd > 0){
12229 labelCfg.cls += ' col-md-' + this.labelmd;
12230 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12233 if(this.labelsm > 0){
12234 labelCfg.cls += ' col-sm-' + this.labelsm;
12235 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12238 if(this.labelxs > 0){
12239 labelCfg.cls += ' col-xs-' + this.labelxs;
12240 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12243 } else if ( this.fieldLabel.length) {
12244 // Roo.log(" label");
12249 //cls : 'input-group-addon',
12250 html : this.fieldLabel
12258 if(this.indicatorpos == 'right'){
12266 html : this.fieldLabel
12280 // Roo.log(" no label && no align");
12287 ['xs','sm','md','lg'].map(function(size){
12288 if (settings[size]) {
12289 cfg.cls += ' col-' + size + '-' + settings[size];
12300 onResize : function(w, h){
12301 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12302 // if(typeof w == 'number'){
12303 // var x = w - this.trigger.getWidth();
12304 // this.inputEl().setWidth(this.adjustWidth('input', x));
12305 // this.trigger.setStyle('left', x+'px');
12310 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12313 getResizeEl : function(){
12314 return this.inputEl();
12318 getPositionEl : function(){
12319 return this.inputEl();
12323 alignErrorIcon : function(){
12324 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12328 initEvents : function(){
12332 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12333 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12334 if(!this.multiple && this.showToggleBtn){
12335 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12336 if(this.hideTrigger){
12337 this.trigger.setDisplayed(false);
12339 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12343 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12346 if(this.removable && !this.editable && !this.tickable){
12347 var close = this.closeTriggerEl();
12350 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12351 close.on('click', this.removeBtnClick, this, close);
12355 //this.trigger.addClassOnOver('x-form-trigger-over');
12356 //this.trigger.addClassOnClick('x-form-trigger-click');
12359 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12363 closeTriggerEl : function()
12365 var close = this.el.select('.roo-combo-removable-btn', true).first();
12366 return close ? close : false;
12369 removeBtnClick : function(e, h, el)
12371 e.preventDefault();
12373 if(this.fireEvent("remove", this) !== false){
12375 this.fireEvent("afterremove", this)
12379 createList : function()
12381 this.list = Roo.get(document.body).createChild({
12382 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12383 cls: 'typeahead typeahead-long dropdown-menu shadow',
12384 style: 'display:none'
12387 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12392 initTrigger : function(){
12397 onDestroy : function(){
12399 this.trigger.removeAllListeners();
12400 // this.trigger.remove();
12403 // this.wrap.remove();
12405 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12409 onFocus : function(){
12410 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12412 if(!this.mimicing){
12413 this.wrap.addClass('x-trigger-wrap-focus');
12414 this.mimicing = true;
12415 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12416 if(this.monitorTab){
12417 this.el.on("keydown", this.checkTab, this);
12424 checkTab : function(e){
12425 if(e.getKey() == e.TAB){
12426 this.triggerBlur();
12431 onBlur : function(){
12436 mimicBlur : function(e, t){
12438 if(!this.wrap.contains(t) && this.validateBlur()){
12439 this.triggerBlur();
12445 triggerBlur : function(){
12446 this.mimicing = false;
12447 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12448 if(this.monitorTab){
12449 this.el.un("keydown", this.checkTab, this);
12451 //this.wrap.removeClass('x-trigger-wrap-focus');
12452 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12456 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12457 validateBlur : function(e, t){
12462 onDisable : function(){
12463 this.inputEl().dom.disabled = true;
12464 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12466 // this.wrap.addClass('x-item-disabled');
12471 onEnable : function(){
12472 this.inputEl().dom.disabled = false;
12473 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12475 // this.el.removeClass('x-item-disabled');
12480 onShow : function(){
12481 var ae = this.getActionEl();
12484 ae.dom.style.display = '';
12485 ae.dom.style.visibility = 'visible';
12491 onHide : function(){
12492 var ae = this.getActionEl();
12493 ae.dom.style.display = 'none';
12497 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12498 * by an implementing function.
12500 * @param {EventObject} e
12502 onTriggerClick : Roo.emptyFn
12510 * @class Roo.bootstrap.CardUploader
12511 * @extends Roo.bootstrap.Button
12512 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12513 * @cfg {Number} errorTimeout default 3000
12514 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12515 * @cfg {Array} html The button text.
12519 * Create a new CardUploader
12520 * @param {Object} config The config object
12523 Roo.bootstrap.CardUploader = function(config){
12527 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12530 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12538 * When a image is clicked on - and needs to display a slideshow or similar..
12539 * @param {Roo.bootstrap.Card} this
12540 * @param {Object} The image information data
12546 * When a the download link is clicked
12547 * @param {Roo.bootstrap.Card} this
12548 * @param {Object} The image information data contains
12555 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12558 errorTimeout : 3000,
12562 fileCollection : false,
12565 getAutoCreate : function()
12569 cls :'form-group' ,
12574 //cls : 'input-group-addon',
12575 html : this.fieldLabel
12583 value : this.value,
12584 cls : 'd-none form-control'
12589 multiple : 'multiple',
12591 cls : 'd-none roo-card-upload-selector'
12595 cls : 'roo-card-uploader-button-container w-100 mb-2'
12598 cls : 'card-columns roo-card-uploader-container'
12608 getChildContainer : function() /// what children are added to.
12610 return this.containerEl;
12613 getButtonContainer : function() /// what children are added to.
12615 return this.el.select(".roo-card-uploader-button-container").first();
12618 initEvents : function()
12621 Roo.bootstrap.Input.prototype.initEvents.call(this);
12625 xns: Roo.bootstrap,
12628 container_method : 'getButtonContainer' ,
12629 html : this.html, // fix changable?
12632 'click' : function(btn, e) {
12641 this.urlAPI = (window.createObjectURL && window) ||
12642 (window.URL && URL.revokeObjectURL && URL) ||
12643 (window.webkitURL && webkitURL);
12648 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12650 this.selectorEl.on('change', this.onFileSelected, this);
12653 this.images.forEach(function(img) {
12656 this.images = false;
12658 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12664 onClick : function(e)
12666 e.preventDefault();
12668 this.selectorEl.dom.click();
12672 onFileSelected : function(e)
12674 e.preventDefault();
12676 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12680 Roo.each(this.selectorEl.dom.files, function(file){
12681 this.addFile(file);
12690 addFile : function(file)
12693 if(typeof(file) === 'string'){
12694 throw "Add file by name?"; // should not happen
12698 if(!file || !this.urlAPI){
12708 var url = _this.urlAPI.createObjectURL( file);
12711 id : Roo.bootstrap.CardUploader.ID--,
12712 is_uploaded : false,
12716 mimetype : file.type,
12724 * addCard - add an Attachment to the uploader
12725 * @param data - the data about the image to upload
12729 title : "Title of file",
12730 is_uploaded : false,
12731 src : "http://.....",
12732 srcfile : { the File upload object },
12733 mimetype : file.type,
12736 .. any other data...
12742 addCard : function (data)
12744 // hidden input element?
12745 // if the file is not an image...
12746 //then we need to use something other that and header_image
12751 xns : Roo.bootstrap,
12752 xtype : 'CardFooter',
12755 xns : Roo.bootstrap,
12761 xns : Roo.bootstrap,
12763 html : String.format("<small>{0}</small>", data.title),
12764 cls : 'col-10 text-left',
12769 click : function() {
12771 t.fireEvent( "download", t, data );
12777 xns : Roo.bootstrap,
12779 style: 'max-height: 28px; ',
12785 click : function() {
12786 t.removeCard(data.id)
12798 var cn = this.addxtype(
12801 xns : Roo.bootstrap,
12804 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12805 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12806 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12811 initEvents : function() {
12812 Roo.bootstrap.Card.prototype.initEvents.call(this);
12814 this.imgEl = this.el.select('.card-img-top').first();
12816 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12817 this.imgEl.set({ 'pointer' : 'cursor' });
12820 this.getCardFooter().addClass('p-1');
12827 // dont' really need ot update items.
12828 // this.items.push(cn);
12829 this.fileCollection.add(cn);
12831 if (!data.srcfile) {
12832 this.updateInput();
12837 var reader = new FileReader();
12838 reader.addEventListener("load", function() {
12839 data.srcdata = reader.result;
12842 reader.readAsDataURL(data.srcfile);
12847 removeCard : function(id)
12850 var card = this.fileCollection.get(id);
12851 card.data.is_deleted = 1;
12852 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12853 //this.fileCollection.remove(card);
12854 //this.items = this.items.filter(function(e) { return e != card });
12855 // dont' really need ot update items.
12856 card.el.dom.parentNode.removeChild(card.el.dom);
12857 this.updateInput();
12863 this.fileCollection.each(function(card) {
12864 if (card.el.dom && card.el.dom.parentNode) {
12865 card.el.dom.parentNode.removeChild(card.el.dom);
12868 this.fileCollection.clear();
12869 this.updateInput();
12872 updateInput : function()
12875 this.fileCollection.each(function(e) {
12879 this.inputEl().dom.value = JSON.stringify(data);
12889 Roo.bootstrap.CardUploader.ID = -1;/*
12891 * Ext JS Library 1.1.1
12892 * Copyright(c) 2006-2007, Ext JS, LLC.
12894 * Originally Released Under LGPL - original licence link has changed is not relivant.
12897 * <script type="text/javascript">
12902 * @class Roo.data.SortTypes
12904 * Defines the default sorting (casting?) comparison functions used when sorting data.
12906 Roo.data.SortTypes = {
12908 * Default sort that does nothing
12909 * @param {Mixed} s The value being converted
12910 * @return {Mixed} The comparison value
12912 none : function(s){
12917 * The regular expression used to strip tags
12921 stripTagsRE : /<\/?[^>]+>/gi,
12924 * Strips all HTML tags to sort on text only
12925 * @param {Mixed} s The value being converted
12926 * @return {String} The comparison value
12928 asText : function(s){
12929 return String(s).replace(this.stripTagsRE, "");
12933 * Strips all HTML tags to sort on text only - Case insensitive
12934 * @param {Mixed} s The value being converted
12935 * @return {String} The comparison value
12937 asUCText : function(s){
12938 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12942 * Case insensitive string
12943 * @param {Mixed} s The value being converted
12944 * @return {String} The comparison value
12946 asUCString : function(s) {
12947 return String(s).toUpperCase();
12952 * @param {Mixed} s The value being converted
12953 * @return {Number} The comparison value
12955 asDate : function(s) {
12959 if(s instanceof Date){
12960 return s.getTime();
12962 return Date.parse(String(s));
12967 * @param {Mixed} s The value being converted
12968 * @return {Float} The comparison value
12970 asFloat : function(s) {
12971 var val = parseFloat(String(s).replace(/,/g, ""));
12980 * @param {Mixed} s The value being converted
12981 * @return {Number} The comparison value
12983 asInt : function(s) {
12984 var val = parseInt(String(s).replace(/,/g, ""));
12992 * Ext JS Library 1.1.1
12993 * Copyright(c) 2006-2007, Ext JS, LLC.
12995 * Originally Released Under LGPL - original licence link has changed is not relivant.
12998 * <script type="text/javascript">
13002 * @class Roo.data.Record
13003 * Instances of this class encapsulate both record <em>definition</em> information, and record
13004 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13005 * to access Records cached in an {@link Roo.data.Store} object.<br>
13007 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13008 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13011 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13013 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13014 * {@link #create}. The parameters are the same.
13015 * @param {Array} data An associative Array of data values keyed by the field name.
13016 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13017 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13018 * not specified an integer id is generated.
13020 Roo.data.Record = function(data, id){
13021 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13026 * Generate a constructor for a specific record layout.
13027 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13028 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13029 * Each field definition object may contain the following properties: <ul>
13030 * <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,
13031 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13032 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13033 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13034 * is being used, then this is a string containing the javascript expression to reference the data relative to
13035 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13036 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13037 * this may be omitted.</p></li>
13038 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13039 * <ul><li>auto (Default, implies no conversion)</li>
13044 * <li>date</li></ul></p></li>
13045 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13046 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13047 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13048 * by the Reader into an object that will be stored in the Record. It is passed the
13049 * following parameters:<ul>
13050 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13052 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13054 * <br>usage:<br><pre><code>
13055 var TopicRecord = Roo.data.Record.create(
13056 {name: 'title', mapping: 'topic_title'},
13057 {name: 'author', mapping: 'username'},
13058 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13059 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13060 {name: 'lastPoster', mapping: 'user2'},
13061 {name: 'excerpt', mapping: 'post_text'}
13064 var myNewRecord = new TopicRecord({
13065 title: 'Do my job please',
13068 lastPost: new Date(),
13069 lastPoster: 'Animal',
13070 excerpt: 'No way dude!'
13072 myStore.add(myNewRecord);
13077 Roo.data.Record.create = function(o){
13078 var f = function(){
13079 f.superclass.constructor.apply(this, arguments);
13081 Roo.extend(f, Roo.data.Record);
13082 var p = f.prototype;
13083 p.fields = new Roo.util.MixedCollection(false, function(field){
13086 for(var i = 0, len = o.length; i < len; i++){
13087 p.fields.add(new Roo.data.Field(o[i]));
13089 f.getField = function(name){
13090 return p.fields.get(name);
13095 Roo.data.Record.AUTO_ID = 1000;
13096 Roo.data.Record.EDIT = 'edit';
13097 Roo.data.Record.REJECT = 'reject';
13098 Roo.data.Record.COMMIT = 'commit';
13100 Roo.data.Record.prototype = {
13102 * Readonly flag - true if this record has been modified.
13111 join : function(store){
13112 this.store = store;
13116 * Set the named field to the specified value.
13117 * @param {String} name The name of the field to set.
13118 * @param {Object} value The value to set the field to.
13120 set : function(name, value){
13121 if(this.data[name] == value){
13125 if(!this.modified){
13126 this.modified = {};
13128 if(typeof this.modified[name] == 'undefined'){
13129 this.modified[name] = this.data[name];
13131 this.data[name] = value;
13132 if(!this.editing && this.store){
13133 this.store.afterEdit(this);
13138 * Get the value of the named field.
13139 * @param {String} name The name of the field to get the value of.
13140 * @return {Object} The value of the field.
13142 get : function(name){
13143 return this.data[name];
13147 beginEdit : function(){
13148 this.editing = true;
13149 this.modified = {};
13153 cancelEdit : function(){
13154 this.editing = false;
13155 delete this.modified;
13159 endEdit : function(){
13160 this.editing = false;
13161 if(this.dirty && this.store){
13162 this.store.afterEdit(this);
13167 * Usually called by the {@link Roo.data.Store} which owns the Record.
13168 * Rejects all changes made to the Record since either creation, or the last commit operation.
13169 * Modified fields are reverted to their original values.
13171 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13172 * of reject operations.
13174 reject : function(){
13175 var m = this.modified;
13177 if(typeof m[n] != "function"){
13178 this.data[n] = m[n];
13181 this.dirty = false;
13182 delete this.modified;
13183 this.editing = false;
13185 this.store.afterReject(this);
13190 * Usually called by the {@link Roo.data.Store} which owns the Record.
13191 * Commits all changes made to the Record since either creation, or the last commit operation.
13193 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13194 * of commit operations.
13196 commit : function(){
13197 this.dirty = false;
13198 delete this.modified;
13199 this.editing = false;
13201 this.store.afterCommit(this);
13206 hasError : function(){
13207 return this.error != null;
13211 clearError : function(){
13216 * Creates a copy of this record.
13217 * @param {String} id (optional) A new record id if you don't want to use this record's id
13220 copy : function(newId) {
13221 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13225 * Ext JS Library 1.1.1
13226 * Copyright(c) 2006-2007, Ext JS, LLC.
13228 * Originally Released Under LGPL - original licence link has changed is not relivant.
13231 * <script type="text/javascript">
13237 * @class Roo.data.Store
13238 * @extends Roo.util.Observable
13239 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13240 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13242 * 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
13243 * has no knowledge of the format of the data returned by the Proxy.<br>
13245 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13246 * instances from the data object. These records are cached and made available through accessor functions.
13248 * Creates a new Store.
13249 * @param {Object} config A config object containing the objects needed for the Store to access data,
13250 * and read the data into Records.
13252 Roo.data.Store = function(config){
13253 this.data = new Roo.util.MixedCollection(false);
13254 this.data.getKey = function(o){
13257 this.baseParams = {};
13259 this.paramNames = {
13264 "multisort" : "_multisort"
13267 if(config && config.data){
13268 this.inlineData = config.data;
13269 delete config.data;
13272 Roo.apply(this, config);
13274 if(this.reader){ // reader passed
13275 this.reader = Roo.factory(this.reader, Roo.data);
13276 this.reader.xmodule = this.xmodule || false;
13277 if(!this.recordType){
13278 this.recordType = this.reader.recordType;
13280 if(this.reader.onMetaChange){
13281 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13285 if(this.recordType){
13286 this.fields = this.recordType.prototype.fields;
13288 this.modified = [];
13292 * @event datachanged
13293 * Fires when the data cache has changed, and a widget which is using this Store
13294 * as a Record cache should refresh its view.
13295 * @param {Store} this
13297 datachanged : true,
13299 * @event metachange
13300 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13301 * @param {Store} this
13302 * @param {Object} meta The JSON metadata
13307 * Fires when Records have been added to the Store
13308 * @param {Store} this
13309 * @param {Roo.data.Record[]} records The array of Records added
13310 * @param {Number} index The index at which the record(s) were added
13315 * Fires when a Record has been removed from the Store
13316 * @param {Store} this
13317 * @param {Roo.data.Record} record The Record that was removed
13318 * @param {Number} index The index at which the record was removed
13323 * Fires when a Record has been updated
13324 * @param {Store} this
13325 * @param {Roo.data.Record} record The Record that was updated
13326 * @param {String} operation The update operation being performed. Value may be one of:
13328 Roo.data.Record.EDIT
13329 Roo.data.Record.REJECT
13330 Roo.data.Record.COMMIT
13336 * Fires when the data cache has been cleared.
13337 * @param {Store} this
13341 * @event beforeload
13342 * Fires before a request is made for a new data object. If the beforeload handler returns false
13343 * the load action will be canceled.
13344 * @param {Store} this
13345 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13349 * @event beforeloadadd
13350 * Fires after a new set of Records has been loaded.
13351 * @param {Store} this
13352 * @param {Roo.data.Record[]} records The Records that were loaded
13353 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13355 beforeloadadd : true,
13358 * Fires after a new set of Records has been loaded, before they are added to the store.
13359 * @param {Store} this
13360 * @param {Roo.data.Record[]} records The Records that were loaded
13361 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13362 * @params {Object} return from reader
13366 * @event loadexception
13367 * Fires if an exception occurs in the Proxy during loading.
13368 * Called with the signature of the Proxy's "loadexception" event.
13369 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13372 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13373 * @param {Object} load options
13374 * @param {Object} jsonData from your request (normally this contains the Exception)
13376 loadexception : true
13380 this.proxy = Roo.factory(this.proxy, Roo.data);
13381 this.proxy.xmodule = this.xmodule || false;
13382 this.relayEvents(this.proxy, ["loadexception"]);
13384 this.sortToggle = {};
13385 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13387 Roo.data.Store.superclass.constructor.call(this);
13389 if(this.inlineData){
13390 this.loadData(this.inlineData);
13391 delete this.inlineData;
13395 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13397 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13398 * without a remote query - used by combo/forms at present.
13402 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13405 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13408 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13409 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13412 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13413 * on any HTTP request
13416 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13419 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13423 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13424 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13426 remoteSort : false,
13429 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13430 * loaded or when a record is removed. (defaults to false).
13432 pruneModifiedRecords : false,
13435 lastOptions : null,
13438 * Add Records to the Store and fires the add event.
13439 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13441 add : function(records){
13442 records = [].concat(records);
13443 for(var i = 0, len = records.length; i < len; i++){
13444 records[i].join(this);
13446 var index = this.data.length;
13447 this.data.addAll(records);
13448 this.fireEvent("add", this, records, index);
13452 * Remove a Record from the Store and fires the remove event.
13453 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13455 remove : function(record){
13456 var index = this.data.indexOf(record);
13457 this.data.removeAt(index);
13459 if(this.pruneModifiedRecords){
13460 this.modified.remove(record);
13462 this.fireEvent("remove", this, record, index);
13466 * Remove all Records from the Store and fires the clear event.
13468 removeAll : function(){
13470 if(this.pruneModifiedRecords){
13471 this.modified = [];
13473 this.fireEvent("clear", this);
13477 * Inserts Records to the Store at the given index and fires the add event.
13478 * @param {Number} index The start index at which to insert the passed Records.
13479 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13481 insert : function(index, records){
13482 records = [].concat(records);
13483 for(var i = 0, len = records.length; i < len; i++){
13484 this.data.insert(index, records[i]);
13485 records[i].join(this);
13487 this.fireEvent("add", this, records, index);
13491 * Get the index within the cache of the passed Record.
13492 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13493 * @return {Number} The index of the passed Record. Returns -1 if not found.
13495 indexOf : function(record){
13496 return this.data.indexOf(record);
13500 * Get the index within the cache of the Record with the passed id.
13501 * @param {String} id The id of the Record to find.
13502 * @return {Number} The index of the Record. Returns -1 if not found.
13504 indexOfId : function(id){
13505 return this.data.indexOfKey(id);
13509 * Get the Record with the specified id.
13510 * @param {String} id The id of the Record to find.
13511 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13513 getById : function(id){
13514 return this.data.key(id);
13518 * Get the Record at the specified index.
13519 * @param {Number} index The index of the Record to find.
13520 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13522 getAt : function(index){
13523 return this.data.itemAt(index);
13527 * Returns a range of Records between specified indices.
13528 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13529 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13530 * @return {Roo.data.Record[]} An array of Records
13532 getRange : function(start, end){
13533 return this.data.getRange(start, end);
13537 storeOptions : function(o){
13538 o = Roo.apply({}, o);
13541 this.lastOptions = o;
13545 * Loads the Record cache from the configured Proxy using the configured Reader.
13547 * If using remote paging, then the first load call must specify the <em>start</em>
13548 * and <em>limit</em> properties in the options.params property to establish the initial
13549 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13551 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13552 * and this call will return before the new data has been loaded. Perform any post-processing
13553 * in a callback function, or in a "load" event handler.</strong>
13555 * @param {Object} options An object containing properties which control loading options:<ul>
13556 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13557 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13558 * passed the following arguments:<ul>
13559 * <li>r : Roo.data.Record[]</li>
13560 * <li>options: Options object from the load call</li>
13561 * <li>success: Boolean success indicator</li></ul></li>
13562 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13563 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13566 load : function(options){
13567 options = options || {};
13568 if(this.fireEvent("beforeload", this, options) !== false){
13569 this.storeOptions(options);
13570 var p = Roo.apply(options.params || {}, this.baseParams);
13571 // if meta was not loaded from remote source.. try requesting it.
13572 if (!this.reader.metaFromRemote) {
13573 p._requestMeta = 1;
13575 if(this.sortInfo && this.remoteSort){
13576 var pn = this.paramNames;
13577 p[pn["sort"]] = this.sortInfo.field;
13578 p[pn["dir"]] = this.sortInfo.direction;
13580 if (this.multiSort) {
13581 var pn = this.paramNames;
13582 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13585 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13590 * Reloads the Record cache from the configured Proxy using the configured Reader and
13591 * the options from the last load operation performed.
13592 * @param {Object} options (optional) An object containing properties which may override the options
13593 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13594 * the most recently used options are reused).
13596 reload : function(options){
13597 this.load(Roo.applyIf(options||{}, this.lastOptions));
13601 // Called as a callback by the Reader during a load operation.
13602 loadRecords : function(o, options, success){
13603 if(!o || success === false){
13604 if(success !== false){
13605 this.fireEvent("load", this, [], options, o);
13607 if(options.callback){
13608 options.callback.call(options.scope || this, [], options, false);
13612 // if data returned failure - throw an exception.
13613 if (o.success === false) {
13614 // show a message if no listener is registered.
13615 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13616 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13618 // loadmask wil be hooked into this..
13619 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13622 var r = o.records, t = o.totalRecords || r.length;
13624 this.fireEvent("beforeloadadd", this, r, options, o);
13626 if(!options || options.add !== true){
13627 if(this.pruneModifiedRecords){
13628 this.modified = [];
13630 for(var i = 0, len = r.length; i < len; i++){
13634 this.data = this.snapshot;
13635 delete this.snapshot;
13638 this.data.addAll(r);
13639 this.totalLength = t;
13641 this.fireEvent("datachanged", this);
13643 this.totalLength = Math.max(t, this.data.length+r.length);
13647 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13649 var e = new Roo.data.Record({});
13651 e.set(this.parent.displayField, this.parent.emptyTitle);
13652 e.set(this.parent.valueField, '');
13657 this.fireEvent("load", this, r, options, o);
13658 if(options.callback){
13659 options.callback.call(options.scope || this, r, options, true);
13665 * Loads data from a passed data block. A Reader which understands the format of the data
13666 * must have been configured in the constructor.
13667 * @param {Object} data The data block from which to read the Records. The format of the data expected
13668 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13669 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13671 loadData : function(o, append){
13672 var r = this.reader.readRecords(o);
13673 this.loadRecords(r, {add: append}, true);
13677 * using 'cn' the nested child reader read the child array into it's child stores.
13678 * @param {Object} rec The record with a 'children array
13680 loadDataFromChildren : function(rec)
13682 this.loadData(this.reader.toLoadData(rec));
13687 * Gets the number of cached records.
13689 * <em>If using paging, this may not be the total size of the dataset. If the data object
13690 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13691 * the data set size</em>
13693 getCount : function(){
13694 return this.data.length || 0;
13698 * Gets the total number of records in the dataset as returned by the server.
13700 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13701 * the dataset size</em>
13703 getTotalCount : function(){
13704 return this.totalLength || 0;
13708 * Returns the sort state of the Store as an object with two properties:
13710 field {String} The name of the field by which the Records are sorted
13711 direction {String} The sort order, "ASC" or "DESC"
13714 getSortState : function(){
13715 return this.sortInfo;
13719 applySort : function(){
13720 if(this.sortInfo && !this.remoteSort){
13721 var s = this.sortInfo, f = s.field;
13722 var st = this.fields.get(f).sortType;
13723 var fn = function(r1, r2){
13724 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13725 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13727 this.data.sort(s.direction, fn);
13728 if(this.snapshot && this.snapshot != this.data){
13729 this.snapshot.sort(s.direction, fn);
13735 * Sets the default sort column and order to be used by the next load operation.
13736 * @param {String} fieldName The name of the field to sort by.
13737 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13739 setDefaultSort : function(field, dir){
13740 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13744 * Sort the Records.
13745 * If remote sorting is used, the sort is performed on the server, and the cache is
13746 * reloaded. If local sorting is used, the cache is sorted internally.
13747 * @param {String} fieldName The name of the field to sort by.
13748 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13750 sort : function(fieldName, dir){
13751 var f = this.fields.get(fieldName);
13753 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13755 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13756 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13761 this.sortToggle[f.name] = dir;
13762 this.sortInfo = {field: f.name, direction: dir};
13763 if(!this.remoteSort){
13765 this.fireEvent("datachanged", this);
13767 this.load(this.lastOptions);
13772 * Calls the specified function for each of the Records in the cache.
13773 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13774 * Returning <em>false</em> aborts and exits the iteration.
13775 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13777 each : function(fn, scope){
13778 this.data.each(fn, scope);
13782 * Gets all records modified since the last commit. Modified records are persisted across load operations
13783 * (e.g., during paging).
13784 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13786 getModifiedRecords : function(){
13787 return this.modified;
13791 createFilterFn : function(property, value, anyMatch){
13792 if(!value.exec){ // not a regex
13793 value = String(value);
13794 if(value.length == 0){
13797 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13799 return function(r){
13800 return value.test(r.data[property]);
13805 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13806 * @param {String} property A field on your records
13807 * @param {Number} start The record index to start at (defaults to 0)
13808 * @param {Number} end The last record index to include (defaults to length - 1)
13809 * @return {Number} The sum
13811 sum : function(property, start, end){
13812 var rs = this.data.items, v = 0;
13813 start = start || 0;
13814 end = (end || end === 0) ? end : rs.length-1;
13816 for(var i = start; i <= end; i++){
13817 v += (rs[i].data[property] || 0);
13823 * Filter the records by a specified property.
13824 * @param {String} field A field on your records
13825 * @param {String/RegExp} value Either a string that the field
13826 * should start with or a RegExp to test against the field
13827 * @param {Boolean} anyMatch True to match any part not just the beginning
13829 filter : function(property, value, anyMatch){
13830 var fn = this.createFilterFn(property, value, anyMatch);
13831 return fn ? this.filterBy(fn) : this.clearFilter();
13835 * Filter by a function. The specified function will be called with each
13836 * record in this data source. If the function returns true the record is included,
13837 * otherwise it is filtered.
13838 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13839 * @param {Object} scope (optional) The scope of the function (defaults to this)
13841 filterBy : function(fn, scope){
13842 this.snapshot = this.snapshot || this.data;
13843 this.data = this.queryBy(fn, scope||this);
13844 this.fireEvent("datachanged", this);
13848 * Query the records by a specified property.
13849 * @param {String} field A field on your records
13850 * @param {String/RegExp} value Either a string that the field
13851 * should start with or a RegExp to test against the field
13852 * @param {Boolean} anyMatch True to match any part not just the beginning
13853 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13855 query : function(property, value, anyMatch){
13856 var fn = this.createFilterFn(property, value, anyMatch);
13857 return fn ? this.queryBy(fn) : this.data.clone();
13861 * Query by a function. The specified function will be called with each
13862 * record in this data source. If the function returns true the record is included
13864 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13865 * @param {Object} scope (optional) The scope of the function (defaults to this)
13866 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13868 queryBy : function(fn, scope){
13869 var data = this.snapshot || this.data;
13870 return data.filterBy(fn, scope||this);
13874 * Collects unique values for a particular dataIndex from this store.
13875 * @param {String} dataIndex The property to collect
13876 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13877 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13878 * @return {Array} An array of the unique values
13880 collect : function(dataIndex, allowNull, bypassFilter){
13881 var d = (bypassFilter === true && this.snapshot) ?
13882 this.snapshot.items : this.data.items;
13883 var v, sv, r = [], l = {};
13884 for(var i = 0, len = d.length; i < len; i++){
13885 v = d[i].data[dataIndex];
13887 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13896 * Revert to a view of the Record cache with no filtering applied.
13897 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13899 clearFilter : function(suppressEvent){
13900 if(this.snapshot && this.snapshot != this.data){
13901 this.data = this.snapshot;
13902 delete this.snapshot;
13903 if(suppressEvent !== true){
13904 this.fireEvent("datachanged", this);
13910 afterEdit : function(record){
13911 if(this.modified.indexOf(record) == -1){
13912 this.modified.push(record);
13914 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13918 afterReject : function(record){
13919 this.modified.remove(record);
13920 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13924 afterCommit : function(record){
13925 this.modified.remove(record);
13926 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13930 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13931 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13933 commitChanges : function(){
13934 var m = this.modified.slice(0);
13935 this.modified = [];
13936 for(var i = 0, len = m.length; i < len; i++){
13942 * Cancel outstanding changes on all changed records.
13944 rejectChanges : function(){
13945 var m = this.modified.slice(0);
13946 this.modified = [];
13947 for(var i = 0, len = m.length; i < len; i++){
13952 onMetaChange : function(meta, rtype, o){
13953 this.recordType = rtype;
13954 this.fields = rtype.prototype.fields;
13955 delete this.snapshot;
13956 this.sortInfo = meta.sortInfo || this.sortInfo;
13957 this.modified = [];
13958 this.fireEvent('metachange', this, this.reader.meta);
13961 moveIndex : function(data, type)
13963 var index = this.indexOf(data);
13965 var newIndex = index + type;
13969 this.insert(newIndex, data);
13974 * Ext JS Library 1.1.1
13975 * Copyright(c) 2006-2007, Ext JS, LLC.
13977 * Originally Released Under LGPL - original licence link has changed is not relivant.
13980 * <script type="text/javascript">
13984 * @class Roo.data.SimpleStore
13985 * @extends Roo.data.Store
13986 * Small helper class to make creating Stores from Array data easier.
13987 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13988 * @cfg {Array} fields An array of field definition objects, or field name strings.
13989 * @cfg {Object} an existing reader (eg. copied from another store)
13990 * @cfg {Array} data The multi-dimensional array of data
13992 * @param {Object} config
13994 Roo.data.SimpleStore = function(config)
13996 Roo.data.SimpleStore.superclass.constructor.call(this, {
13998 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14001 Roo.data.Record.create(config.fields)
14003 proxy : new Roo.data.MemoryProxy(config.data)
14007 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14009 * Ext JS Library 1.1.1
14010 * Copyright(c) 2006-2007, Ext JS, LLC.
14012 * Originally Released Under LGPL - original licence link has changed is not relivant.
14015 * <script type="text/javascript">
14020 * @extends Roo.data.Store
14021 * @class Roo.data.JsonStore
14022 * Small helper class to make creating Stores for JSON data easier. <br/>
14024 var store = new Roo.data.JsonStore({
14025 url: 'get-images.php',
14027 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14030 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14031 * JsonReader and HttpProxy (unless inline data is provided).</b>
14032 * @cfg {Array} fields An array of field definition objects, or field name strings.
14034 * @param {Object} config
14036 Roo.data.JsonStore = function(c){
14037 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14038 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14039 reader: new Roo.data.JsonReader(c, c.fields)
14042 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14044 * Ext JS Library 1.1.1
14045 * Copyright(c) 2006-2007, Ext JS, LLC.
14047 * Originally Released Under LGPL - original licence link has changed is not relivant.
14050 * <script type="text/javascript">
14054 Roo.data.Field = function(config){
14055 if(typeof config == "string"){
14056 config = {name: config};
14058 Roo.apply(this, config);
14061 this.type = "auto";
14064 var st = Roo.data.SortTypes;
14065 // named sortTypes are supported, here we look them up
14066 if(typeof this.sortType == "string"){
14067 this.sortType = st[this.sortType];
14070 // set default sortType for strings and dates
14071 if(!this.sortType){
14074 this.sortType = st.asUCString;
14077 this.sortType = st.asDate;
14080 this.sortType = st.none;
14085 var stripRe = /[\$,%]/g;
14087 // prebuilt conversion function for this field, instead of
14088 // switching every time we're reading a value
14090 var cv, dateFormat = this.dateFormat;
14095 cv = function(v){ return v; };
14098 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14102 return v !== undefined && v !== null && v !== '' ?
14103 parseInt(String(v).replace(stripRe, ""), 10) : '';
14108 return v !== undefined && v !== null && v !== '' ?
14109 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14114 cv = function(v){ return v === true || v === "true" || v == 1; };
14121 if(v instanceof Date){
14125 if(dateFormat == "timestamp"){
14126 return new Date(v*1000);
14128 return Date.parseDate(v, dateFormat);
14130 var parsed = Date.parse(v);
14131 return parsed ? new Date(parsed) : null;
14140 Roo.data.Field.prototype = {
14148 * Ext JS Library 1.1.1
14149 * Copyright(c) 2006-2007, Ext JS, LLC.
14151 * Originally Released Under LGPL - original licence link has changed is not relivant.
14154 * <script type="text/javascript">
14157 // Base class for reading structured data from a data source. This class is intended to be
14158 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14161 * @class Roo.data.DataReader
14162 * Base class for reading structured data from a data source. This class is intended to be
14163 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14166 Roo.data.DataReader = function(meta, recordType){
14170 this.recordType = recordType instanceof Array ?
14171 Roo.data.Record.create(recordType) : recordType;
14174 Roo.data.DataReader.prototype = {
14177 readerType : 'Data',
14179 * Create an empty record
14180 * @param {Object} data (optional) - overlay some values
14181 * @return {Roo.data.Record} record created.
14183 newRow : function(d) {
14185 this.recordType.prototype.fields.each(function(c) {
14187 case 'int' : da[c.name] = 0; break;
14188 case 'date' : da[c.name] = new Date(); break;
14189 case 'float' : da[c.name] = 0.0; break;
14190 case 'boolean' : da[c.name] = false; break;
14191 default : da[c.name] = ""; break;
14195 return new this.recordType(Roo.apply(da, d));
14201 * Ext JS Library 1.1.1
14202 * Copyright(c) 2006-2007, Ext JS, LLC.
14204 * Originally Released Under LGPL - original licence link has changed is not relivant.
14207 * <script type="text/javascript">
14211 * @class Roo.data.DataProxy
14212 * @extends Roo.data.Observable
14213 * This class is an abstract base class for implementations which provide retrieval of
14214 * unformatted data objects.<br>
14216 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14217 * (of the appropriate type which knows how to parse the data object) to provide a block of
14218 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14220 * Custom implementations must implement the load method as described in
14221 * {@link Roo.data.HttpProxy#load}.
14223 Roo.data.DataProxy = function(){
14226 * @event beforeload
14227 * Fires before a network request is made to retrieve a data object.
14228 * @param {Object} This DataProxy object.
14229 * @param {Object} params The params parameter to the load function.
14234 * Fires before the load method's callback is called.
14235 * @param {Object} This DataProxy object.
14236 * @param {Object} o The data object.
14237 * @param {Object} arg The callback argument object passed to the load function.
14241 * @event loadexception
14242 * Fires if an Exception occurs during data retrieval.
14243 * @param {Object} This DataProxy object.
14244 * @param {Object} o The data object.
14245 * @param {Object} arg The callback argument object passed to the load function.
14246 * @param {Object} e The Exception.
14248 loadexception : true
14250 Roo.data.DataProxy.superclass.constructor.call(this);
14253 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14256 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14260 * Ext JS Library 1.1.1
14261 * Copyright(c) 2006-2007, Ext JS, LLC.
14263 * Originally Released Under LGPL - original licence link has changed is not relivant.
14266 * <script type="text/javascript">
14269 * @class Roo.data.MemoryProxy
14270 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14271 * to the Reader when its load method is called.
14273 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14275 Roo.data.MemoryProxy = function(data){
14279 Roo.data.MemoryProxy.superclass.constructor.call(this);
14283 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14286 * Load data from the requested source (in this case an in-memory
14287 * data object passed to the constructor), read the data object into
14288 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14289 * process that block using the passed callback.
14290 * @param {Object} params This parameter is not used by the MemoryProxy class.
14291 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14292 * object into a block of Roo.data.Records.
14293 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14294 * The function must be passed <ul>
14295 * <li>The Record block object</li>
14296 * <li>The "arg" argument from the load function</li>
14297 * <li>A boolean success indicator</li>
14299 * @param {Object} scope The scope in which to call the callback
14300 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14302 load : function(params, reader, callback, scope, arg){
14303 params = params || {};
14306 result = reader.readRecords(params.data ? params.data :this.data);
14308 this.fireEvent("loadexception", this, arg, null, e);
14309 callback.call(scope, null, arg, false);
14312 callback.call(scope, result, arg, true);
14316 update : function(params, records){
14321 * Ext JS Library 1.1.1
14322 * Copyright(c) 2006-2007, Ext JS, LLC.
14324 * Originally Released Under LGPL - original licence link has changed is not relivant.
14327 * <script type="text/javascript">
14330 * @class Roo.data.HttpProxy
14331 * @extends Roo.data.DataProxy
14332 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14333 * configured to reference a certain URL.<br><br>
14335 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14336 * from which the running page was served.<br><br>
14338 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14340 * Be aware that to enable the browser to parse an XML document, the server must set
14341 * the Content-Type header in the HTTP response to "text/xml".
14343 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14344 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14345 * will be used to make the request.
14347 Roo.data.HttpProxy = function(conn){
14348 Roo.data.HttpProxy.superclass.constructor.call(this);
14349 // is conn a conn config or a real conn?
14351 this.useAjax = !conn || !conn.events;
14355 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14356 // thse are take from connection...
14359 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14362 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14363 * extra parameters to each request made by this object. (defaults to undefined)
14366 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14367 * to each request made by this object. (defaults to undefined)
14370 * @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)
14373 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14376 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14382 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14386 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14387 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14388 * a finer-grained basis than the DataProxy events.
14390 getConnection : function(){
14391 return this.useAjax ? Roo.Ajax : this.conn;
14395 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14396 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14397 * process that block using the passed callback.
14398 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14399 * for the request to the remote server.
14400 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14401 * object into a block of Roo.data.Records.
14402 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14403 * The function must be passed <ul>
14404 * <li>The Record block object</li>
14405 * <li>The "arg" argument from the load function</li>
14406 * <li>A boolean success indicator</li>
14408 * @param {Object} scope The scope in which to call the callback
14409 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14411 load : function(params, reader, callback, scope, arg){
14412 if(this.fireEvent("beforeload", this, params) !== false){
14414 params : params || {},
14416 callback : callback,
14421 callback : this.loadResponse,
14425 Roo.applyIf(o, this.conn);
14426 if(this.activeRequest){
14427 Roo.Ajax.abort(this.activeRequest);
14429 this.activeRequest = Roo.Ajax.request(o);
14431 this.conn.request(o);
14434 callback.call(scope||this, null, arg, false);
14439 loadResponse : function(o, success, response){
14440 delete this.activeRequest;
14442 this.fireEvent("loadexception", this, o, response);
14443 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14448 result = o.reader.read(response);
14450 this.fireEvent("loadexception", this, o, response, e);
14451 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14455 this.fireEvent("load", this, o, o.request.arg);
14456 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14460 update : function(dataSet){
14465 updateResponse : function(dataSet){
14470 * Ext JS Library 1.1.1
14471 * Copyright(c) 2006-2007, Ext JS, LLC.
14473 * Originally Released Under LGPL - original licence link has changed is not relivant.
14476 * <script type="text/javascript">
14480 * @class Roo.data.ScriptTagProxy
14481 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14482 * other than the originating domain of the running page.<br><br>
14484 * <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
14485 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14487 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14488 * source code that is used as the source inside a <script> tag.<br><br>
14490 * In order for the browser to process the returned data, the server must wrap the data object
14491 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14492 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14493 * depending on whether the callback name was passed:
14496 boolean scriptTag = false;
14497 String cb = request.getParameter("callback");
14500 response.setContentType("text/javascript");
14502 response.setContentType("application/x-json");
14504 Writer out = response.getWriter();
14506 out.write(cb + "(");
14508 out.print(dataBlock.toJsonString());
14515 * @param {Object} config A configuration object.
14517 Roo.data.ScriptTagProxy = function(config){
14518 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14519 Roo.apply(this, config);
14520 this.head = document.getElementsByTagName("head")[0];
14523 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14525 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14527 * @cfg {String} url The URL from which to request the data object.
14530 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14534 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14535 * the server the name of the callback function set up by the load call to process the returned data object.
14536 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14537 * javascript output which calls this named function passing the data object as its only parameter.
14539 callbackParam : "callback",
14541 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14542 * name to the request.
14547 * Load data from the configured URL, read the data object into
14548 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14549 * process that block using the passed callback.
14550 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14551 * for the request to the remote server.
14552 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14553 * object into a block of Roo.data.Records.
14554 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14555 * The function must be passed <ul>
14556 * <li>The Record block object</li>
14557 * <li>The "arg" argument from the load function</li>
14558 * <li>A boolean success indicator</li>
14560 * @param {Object} scope The scope in which to call the callback
14561 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14563 load : function(params, reader, callback, scope, arg){
14564 if(this.fireEvent("beforeload", this, params) !== false){
14566 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14568 var url = this.url;
14569 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14571 url += "&_dc=" + (new Date().getTime());
14573 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14576 cb : "stcCallback"+transId,
14577 scriptId : "stcScript"+transId,
14581 callback : callback,
14587 window[trans.cb] = function(o){
14588 conn.handleResponse(o, trans);
14591 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14593 if(this.autoAbort !== false){
14597 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14599 var script = document.createElement("script");
14600 script.setAttribute("src", url);
14601 script.setAttribute("type", "text/javascript");
14602 script.setAttribute("id", trans.scriptId);
14603 this.head.appendChild(script);
14605 this.trans = trans;
14607 callback.call(scope||this, null, arg, false);
14612 isLoading : function(){
14613 return this.trans ? true : false;
14617 * Abort the current server request.
14619 abort : function(){
14620 if(this.isLoading()){
14621 this.destroyTrans(this.trans);
14626 destroyTrans : function(trans, isLoaded){
14627 this.head.removeChild(document.getElementById(trans.scriptId));
14628 clearTimeout(trans.timeoutId);
14630 window[trans.cb] = undefined;
14632 delete window[trans.cb];
14635 // if hasn't been loaded, wait for load to remove it to prevent script error
14636 window[trans.cb] = function(){
14637 window[trans.cb] = undefined;
14639 delete window[trans.cb];
14646 handleResponse : function(o, trans){
14647 this.trans = false;
14648 this.destroyTrans(trans, true);
14651 result = trans.reader.readRecords(o);
14653 this.fireEvent("loadexception", this, o, trans.arg, e);
14654 trans.callback.call(trans.scope||window, null, trans.arg, false);
14657 this.fireEvent("load", this, o, trans.arg);
14658 trans.callback.call(trans.scope||window, result, trans.arg, true);
14662 handleFailure : function(trans){
14663 this.trans = false;
14664 this.destroyTrans(trans, false);
14665 this.fireEvent("loadexception", this, null, trans.arg);
14666 trans.callback.call(trans.scope||window, null, trans.arg, false);
14670 * Ext JS Library 1.1.1
14671 * Copyright(c) 2006-2007, Ext JS, LLC.
14673 * Originally Released Under LGPL - original licence link has changed is not relivant.
14676 * <script type="text/javascript">
14680 * @class Roo.data.JsonReader
14681 * @extends Roo.data.DataReader
14682 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14683 * based on mappings in a provided Roo.data.Record constructor.
14685 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14686 * in the reply previously.
14691 var RecordDef = Roo.data.Record.create([
14692 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14693 {name: 'occupation'} // This field will use "occupation" as the mapping.
14695 var myReader = new Roo.data.JsonReader({
14696 totalProperty: "results", // The property which contains the total dataset size (optional)
14697 root: "rows", // The property which contains an Array of row objects
14698 id: "id" // The property within each row object that provides an ID for the record (optional)
14702 * This would consume a JSON file like this:
14704 { 'results': 2, 'rows': [
14705 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14706 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14709 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14710 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14711 * paged from the remote server.
14712 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14713 * @cfg {String} root name of the property which contains the Array of row objects.
14714 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14715 * @cfg {Array} fields Array of field definition objects
14717 * Create a new JsonReader
14718 * @param {Object} meta Metadata configuration options
14719 * @param {Object} recordType Either an Array of field definition objects,
14720 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14722 Roo.data.JsonReader = function(meta, recordType){
14725 // set some defaults:
14726 Roo.applyIf(meta, {
14727 totalProperty: 'total',
14728 successProperty : 'success',
14733 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14735 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14737 readerType : 'Json',
14740 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14741 * Used by Store query builder to append _requestMeta to params.
14744 metaFromRemote : false,
14746 * This method is only used by a DataProxy which has retrieved data from a remote server.
14747 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14748 * @return {Object} data A data block which is used by an Roo.data.Store object as
14749 * a cache of Roo.data.Records.
14751 read : function(response){
14752 var json = response.responseText;
14754 var o = /* eval:var:o */ eval("("+json+")");
14756 throw {message: "JsonReader.read: Json object not found"};
14762 this.metaFromRemote = true;
14763 this.meta = o.metaData;
14764 this.recordType = Roo.data.Record.create(o.metaData.fields);
14765 this.onMetaChange(this.meta, this.recordType, o);
14767 return this.readRecords(o);
14770 // private function a store will implement
14771 onMetaChange : function(meta, recordType, o){
14778 simpleAccess: function(obj, subsc) {
14785 getJsonAccessor: function(){
14787 return function(expr) {
14789 return(re.test(expr))
14790 ? new Function("obj", "return obj." + expr)
14795 return Roo.emptyFn;
14800 * Create a data block containing Roo.data.Records from an XML document.
14801 * @param {Object} o An object which contains an Array of row objects in the property specified
14802 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14803 * which contains the total size of the dataset.
14804 * @return {Object} data A data block which is used by an Roo.data.Store object as
14805 * a cache of Roo.data.Records.
14807 readRecords : function(o){
14809 * After any data loads, the raw JSON data is available for further custom processing.
14813 var s = this.meta, Record = this.recordType,
14814 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14816 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14818 if(s.totalProperty) {
14819 this.getTotal = this.getJsonAccessor(s.totalProperty);
14821 if(s.successProperty) {
14822 this.getSuccess = this.getJsonAccessor(s.successProperty);
14824 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14826 var g = this.getJsonAccessor(s.id);
14827 this.getId = function(rec) {
14829 return (r === undefined || r === "") ? null : r;
14832 this.getId = function(){return null;};
14835 for(var jj = 0; jj < fl; jj++){
14837 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14838 this.ef[jj] = this.getJsonAccessor(map);
14842 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14843 if(s.totalProperty){
14844 var vt = parseInt(this.getTotal(o), 10);
14849 if(s.successProperty){
14850 var vs = this.getSuccess(o);
14851 if(vs === false || vs === 'false'){
14856 for(var i = 0; i < c; i++){
14859 var id = this.getId(n);
14860 for(var j = 0; j < fl; j++){
14862 var v = this.ef[j](n);
14864 Roo.log('missing convert for ' + f.name);
14868 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14870 var record = new Record(values, id);
14872 records[i] = record;
14878 totalRecords : totalRecords
14881 // used when loading children.. @see loadDataFromChildren
14882 toLoadData: function(rec)
14884 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14885 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14886 return { data : data, total : data.length };
14891 * Ext JS Library 1.1.1
14892 * Copyright(c) 2006-2007, Ext JS, LLC.
14894 * Originally Released Under LGPL - original licence link has changed is not relivant.
14897 * <script type="text/javascript">
14901 * @class Roo.data.ArrayReader
14902 * @extends Roo.data.DataReader
14903 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14904 * Each element of that Array represents a row of data fields. The
14905 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14906 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14910 var RecordDef = Roo.data.Record.create([
14911 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14912 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14914 var myReader = new Roo.data.ArrayReader({
14915 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14919 * This would consume an Array like this:
14921 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14925 * Create a new JsonReader
14926 * @param {Object} meta Metadata configuration options.
14927 * @param {Object|Array} recordType Either an Array of field definition objects
14929 * @cfg {Array} fields Array of field definition objects
14930 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14931 * as specified to {@link Roo.data.Record#create},
14932 * or an {@link Roo.data.Record} object
14935 * created using {@link Roo.data.Record#create}.
14937 Roo.data.ArrayReader = function(meta, recordType)
14939 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14942 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14945 * Create a data block containing Roo.data.Records from an XML document.
14946 * @param {Object} o An Array of row objects which represents the dataset.
14947 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14948 * a cache of Roo.data.Records.
14950 readRecords : function(o)
14952 var sid = this.meta ? this.meta.id : null;
14953 var recordType = this.recordType, fields = recordType.prototype.fields;
14956 for(var i = 0; i < root.length; i++){
14959 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14960 for(var j = 0, jlen = fields.length; j < jlen; j++){
14961 var f = fields.items[j];
14962 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14963 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14965 values[f.name] = v;
14967 var record = new recordType(values, id);
14969 records[records.length] = record;
14973 totalRecords : records.length
14976 // used when loading children.. @see loadDataFromChildren
14977 toLoadData: function(rec)
14979 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14980 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14991 * @class Roo.bootstrap.ComboBox
14992 * @extends Roo.bootstrap.TriggerField
14993 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14994 * @cfg {Boolean} append (true|false) default false
14995 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14996 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14997 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14998 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14999 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15000 * @cfg {Boolean} animate default true
15001 * @cfg {Boolean} emptyResultText only for touch device
15002 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15003 * @cfg {String} emptyTitle default ''
15004 * @cfg {Number} width fixed with? experimental
15006 * Create a new ComboBox.
15007 * @param {Object} config Configuration options
15009 Roo.bootstrap.ComboBox = function(config){
15010 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15014 * Fires when the dropdown list is expanded
15015 * @param {Roo.bootstrap.ComboBox} combo This combo box
15020 * Fires when the dropdown list is collapsed
15021 * @param {Roo.bootstrap.ComboBox} combo This combo box
15025 * @event beforeselect
15026 * Fires before a list item is selected. Return false to cancel the selection.
15027 * @param {Roo.bootstrap.ComboBox} combo This combo box
15028 * @param {Roo.data.Record} record The data record returned from the underlying store
15029 * @param {Number} index The index of the selected item in the dropdown list
15031 'beforeselect' : true,
15034 * Fires when a list item is selected
15035 * @param {Roo.bootstrap.ComboBox} combo This combo box
15036 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15037 * @param {Number} index The index of the selected item in the dropdown list
15041 * @event beforequery
15042 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15043 * The event object passed has these properties:
15044 * @param {Roo.bootstrap.ComboBox} combo This combo box
15045 * @param {String} query The query
15046 * @param {Boolean} forceAll true to force "all" query
15047 * @param {Boolean} cancel true to cancel the query
15048 * @param {Object} e The query event object
15050 'beforequery': true,
15053 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15054 * @param {Roo.bootstrap.ComboBox} combo This combo box
15059 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15060 * @param {Roo.bootstrap.ComboBox} combo This combo box
15061 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15066 * Fires when the remove value from the combobox array
15067 * @param {Roo.bootstrap.ComboBox} combo This combo box
15071 * @event afterremove
15072 * Fires when the remove value from the combobox array
15073 * @param {Roo.bootstrap.ComboBox} combo This combo box
15075 'afterremove' : true,
15077 * @event specialfilter
15078 * Fires when specialfilter
15079 * @param {Roo.bootstrap.ComboBox} combo This combo box
15081 'specialfilter' : true,
15084 * Fires when tick the element
15085 * @param {Roo.bootstrap.ComboBox} combo This combo box
15089 * @event touchviewdisplay
15090 * Fires when touch view require special display (default is using displayField)
15091 * @param {Roo.bootstrap.ComboBox} combo This combo box
15092 * @param {Object} cfg set html .
15094 'touchviewdisplay' : true
15099 this.tickItems = [];
15101 this.selectedIndex = -1;
15102 if(this.mode == 'local'){
15103 if(config.queryDelay === undefined){
15104 this.queryDelay = 10;
15106 if(config.minChars === undefined){
15112 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15115 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15116 * rendering into an Roo.Editor, defaults to false)
15119 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15120 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15123 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15126 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15127 * the dropdown list (defaults to undefined, with no header element)
15131 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15135 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15137 listWidth: undefined,
15139 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15140 * mode = 'remote' or 'text' if mode = 'local')
15142 displayField: undefined,
15145 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15146 * mode = 'remote' or 'value' if mode = 'local').
15147 * Note: use of a valueField requires the user make a selection
15148 * in order for a value to be mapped.
15150 valueField: undefined,
15152 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15157 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15158 * field's data value (defaults to the underlying DOM element's name)
15160 hiddenName: undefined,
15162 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15166 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15168 selectedClass: 'active',
15171 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15175 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15176 * anchor positions (defaults to 'tl-bl')
15178 listAlign: 'tl-bl?',
15180 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15184 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15185 * query specified by the allQuery config option (defaults to 'query')
15187 triggerAction: 'query',
15189 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15190 * (defaults to 4, does not apply if editable = false)
15194 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15195 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15199 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15200 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15204 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15205 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15209 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15210 * when editable = true (defaults to false)
15212 selectOnFocus:false,
15214 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15216 queryParam: 'query',
15218 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15219 * when mode = 'remote' (defaults to 'Loading...')
15221 loadingText: 'Loading...',
15223 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15227 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15231 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15232 * traditional select (defaults to true)
15236 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15240 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15244 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15245 * listWidth has a higher value)
15249 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15250 * allow the user to set arbitrary text into the field (defaults to false)
15252 forceSelection:false,
15254 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15255 * if typeAhead = true (defaults to 250)
15257 typeAheadDelay : 250,
15259 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15260 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15262 valueNotFoundText : undefined,
15264 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15266 blockFocus : false,
15269 * @cfg {Boolean} disableClear Disable showing of clear button.
15271 disableClear : false,
15273 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15275 alwaysQuery : false,
15278 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15283 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15285 invalidClass : "has-warning",
15288 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15290 validClass : "has-success",
15293 * @cfg {Boolean} specialFilter (true|false) special filter default false
15295 specialFilter : false,
15298 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15300 mobileTouchView : true,
15303 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15305 useNativeIOS : false,
15308 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15310 mobile_restrict_height : false,
15312 ios_options : false,
15324 btnPosition : 'right',
15325 triggerList : true,
15326 showToggleBtn : true,
15328 emptyResultText: 'Empty',
15329 triggerText : 'Select',
15333 // element that contains real text value.. (when hidden is used..)
15335 getAutoCreate : function()
15340 * Render classic select for iso
15343 if(Roo.isIOS && this.useNativeIOS){
15344 cfg = this.getAutoCreateNativeIOS();
15352 if(Roo.isTouch && this.mobileTouchView){
15353 cfg = this.getAutoCreateTouchView();
15360 if(!this.tickable){
15361 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15366 * ComboBox with tickable selections
15369 var align = this.labelAlign || this.parentLabelAlign();
15372 cls : 'form-group roo-combobox-tickable' //input-group
15375 var btn_text_select = '';
15376 var btn_text_done = '';
15377 var btn_text_cancel = '';
15379 if (this.btn_text_show) {
15380 btn_text_select = 'Select';
15381 btn_text_done = 'Done';
15382 btn_text_cancel = 'Cancel';
15387 cls : 'tickable-buttons',
15392 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15393 //html : this.triggerText
15394 html: btn_text_select
15400 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15402 html: btn_text_done
15408 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15410 html: btn_text_cancel
15416 buttons.cn.unshift({
15418 cls: 'roo-select2-search-field-input'
15424 Roo.each(buttons.cn, function(c){
15426 c.cls += ' btn-' + _this.size;
15429 if (_this.disabled) {
15436 style : 'display: contents',
15441 cls: 'form-hidden-field'
15445 cls: 'roo-select2-choices',
15449 cls: 'roo-select2-search-field',
15460 cls: 'roo-select2-container input-group roo-select2-container-multi',
15466 // cls: 'typeahead typeahead-long dropdown-menu',
15467 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15472 if(this.hasFeedback && !this.allowBlank){
15476 cls: 'glyphicon form-control-feedback'
15479 combobox.cn.push(feedback);
15486 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15487 tooltip : 'This field is required'
15489 if (Roo.bootstrap.version == 4) {
15492 style : 'display:none'
15495 if (align ==='left' && this.fieldLabel.length) {
15497 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15504 cls : 'control-label col-form-label',
15505 html : this.fieldLabel
15517 var labelCfg = cfg.cn[1];
15518 var contentCfg = cfg.cn[2];
15521 if(this.indicatorpos == 'right'){
15527 cls : 'control-label col-form-label',
15531 html : this.fieldLabel
15547 labelCfg = cfg.cn[0];
15548 contentCfg = cfg.cn[1];
15552 if(this.labelWidth > 12){
15553 labelCfg.style = "width: " + this.labelWidth + 'px';
15555 if(this.width * 1 > 0){
15556 contentCfg.style = "width: " + this.width + 'px';
15558 if(this.labelWidth < 13 && this.labelmd == 0){
15559 this.labelmd = this.labelWidth;
15562 if(this.labellg > 0){
15563 labelCfg.cls += ' col-lg-' + this.labellg;
15564 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15567 if(this.labelmd > 0){
15568 labelCfg.cls += ' col-md-' + this.labelmd;
15569 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15572 if(this.labelsm > 0){
15573 labelCfg.cls += ' col-sm-' + this.labelsm;
15574 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15577 if(this.labelxs > 0){
15578 labelCfg.cls += ' col-xs-' + this.labelxs;
15579 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15583 } else if ( this.fieldLabel.length) {
15584 // Roo.log(" label");
15589 //cls : 'input-group-addon',
15590 html : this.fieldLabel
15595 if(this.indicatorpos == 'right'){
15599 //cls : 'input-group-addon',
15600 html : this.fieldLabel
15610 // Roo.log(" no label && no align");
15617 ['xs','sm','md','lg'].map(function(size){
15618 if (settings[size]) {
15619 cfg.cls += ' col-' + size + '-' + settings[size];
15627 _initEventsCalled : false,
15630 initEvents: function()
15632 if (this._initEventsCalled) { // as we call render... prevent looping...
15635 this._initEventsCalled = true;
15638 throw "can not find store for combo";
15641 this.indicator = this.indicatorEl();
15643 this.store = Roo.factory(this.store, Roo.data);
15644 this.store.parent = this;
15646 // if we are building from html. then this element is so complex, that we can not really
15647 // use the rendered HTML.
15648 // so we have to trash and replace the previous code.
15649 if (Roo.XComponent.build_from_html) {
15650 // remove this element....
15651 var e = this.el.dom, k=0;
15652 while (e ) { e = e.previousSibling; ++k;}
15657 this.rendered = false;
15659 this.render(this.parent().getChildContainer(true), k);
15662 if(Roo.isIOS && this.useNativeIOS){
15663 this.initIOSView();
15671 if(Roo.isTouch && this.mobileTouchView){
15672 this.initTouchView();
15677 this.initTickableEvents();
15681 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15683 if(this.hiddenName){
15685 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15687 this.hiddenField.dom.value =
15688 this.hiddenValue !== undefined ? this.hiddenValue :
15689 this.value !== undefined ? this.value : '';
15691 // prevent input submission
15692 this.el.dom.removeAttribute('name');
15693 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15698 // this.el.dom.setAttribute('autocomplete', 'off');
15701 var cls = 'x-combo-list';
15703 //this.list = new Roo.Layer({
15704 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15710 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15711 _this.list.setWidth(lw);
15714 this.list.on('mouseover', this.onViewOver, this);
15715 this.list.on('mousemove', this.onViewMove, this);
15716 this.list.on('scroll', this.onViewScroll, this);
15719 this.list.swallowEvent('mousewheel');
15720 this.assetHeight = 0;
15723 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15724 this.assetHeight += this.header.getHeight();
15727 this.innerList = this.list.createChild({cls:cls+'-inner'});
15728 this.innerList.on('mouseover', this.onViewOver, this);
15729 this.innerList.on('mousemove', this.onViewMove, this);
15730 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15732 if(this.allowBlank && !this.pageSize && !this.disableClear){
15733 this.footer = this.list.createChild({cls:cls+'-ft'});
15734 this.pageTb = new Roo.Toolbar(this.footer);
15738 this.footer = this.list.createChild({cls:cls+'-ft'});
15739 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15740 {pageSize: this.pageSize});
15744 if (this.pageTb && this.allowBlank && !this.disableClear) {
15746 this.pageTb.add(new Roo.Toolbar.Fill(), {
15747 cls: 'x-btn-icon x-btn-clear',
15749 handler: function()
15752 _this.clearValue();
15753 _this.onSelect(false, -1);
15758 this.assetHeight += this.footer.getHeight();
15763 this.tpl = Roo.bootstrap.version == 4 ?
15764 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15765 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15768 this.view = new Roo.View(this.list, this.tpl, {
15769 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15771 //this.view.wrapEl.setDisplayed(false);
15772 this.view.on('click', this.onViewClick, this);
15775 this.store.on('beforeload', this.onBeforeLoad, this);
15776 this.store.on('load', this.onLoad, this);
15777 this.store.on('loadexception', this.onLoadException, this);
15779 if(this.resizable){
15780 this.resizer = new Roo.Resizable(this.list, {
15781 pinned:true, handles:'se'
15783 this.resizer.on('resize', function(r, w, h){
15784 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15785 this.listWidth = w;
15786 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15787 this.restrictHeight();
15789 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15792 if(!this.editable){
15793 this.editable = true;
15794 this.setEditable(false);
15799 if (typeof(this.events.add.listeners) != 'undefined') {
15801 this.addicon = this.wrap.createChild(
15802 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15804 this.addicon.on('click', function(e) {
15805 this.fireEvent('add', this);
15808 if (typeof(this.events.edit.listeners) != 'undefined') {
15810 this.editicon = this.wrap.createChild(
15811 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15812 if (this.addicon) {
15813 this.editicon.setStyle('margin-left', '40px');
15815 this.editicon.on('click', function(e) {
15817 // we fire even if inothing is selected..
15818 this.fireEvent('edit', this, this.lastData );
15824 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15825 "up" : function(e){
15826 this.inKeyMode = true;
15830 "down" : function(e){
15831 if(!this.isExpanded()){
15832 this.onTriggerClick();
15834 this.inKeyMode = true;
15839 "enter" : function(e){
15840 // this.onViewClick();
15844 if(this.fireEvent("specialkey", this, e)){
15845 this.onViewClick(false);
15851 "esc" : function(e){
15855 "tab" : function(e){
15858 if(this.fireEvent("specialkey", this, e)){
15859 this.onViewClick(false);
15867 doRelay : function(foo, bar, hname){
15868 if(hname == 'down' || this.scope.isExpanded()){
15869 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15878 this.queryDelay = Math.max(this.queryDelay || 10,
15879 this.mode == 'local' ? 10 : 250);
15882 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15884 if(this.typeAhead){
15885 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15887 if(this.editable !== false){
15888 this.inputEl().on("keyup", this.onKeyUp, this);
15890 if(this.forceSelection){
15891 this.inputEl().on('blur', this.doForce, this);
15895 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15896 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15900 initTickableEvents: function()
15904 if(this.hiddenName){
15906 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15908 this.hiddenField.dom.value =
15909 this.hiddenValue !== undefined ? this.hiddenValue :
15910 this.value !== undefined ? this.value : '';
15912 // prevent input submission
15913 this.el.dom.removeAttribute('name');
15914 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15919 // this.list = this.el.select('ul.dropdown-menu',true).first();
15921 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15922 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15923 if(this.triggerList){
15924 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15927 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15928 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15930 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15931 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15933 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15934 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15936 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15937 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15938 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15941 this.cancelBtn.hide();
15946 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15947 _this.list.setWidth(lw);
15950 this.list.on('mouseover', this.onViewOver, this);
15951 this.list.on('mousemove', this.onViewMove, this);
15953 this.list.on('scroll', this.onViewScroll, this);
15956 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15957 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15960 this.view = new Roo.View(this.list, this.tpl, {
15965 selectedClass: this.selectedClass
15968 //this.view.wrapEl.setDisplayed(false);
15969 this.view.on('click', this.onViewClick, this);
15973 this.store.on('beforeload', this.onBeforeLoad, this);
15974 this.store.on('load', this.onLoad, this);
15975 this.store.on('loadexception', this.onLoadException, this);
15978 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15979 "up" : function(e){
15980 this.inKeyMode = true;
15984 "down" : function(e){
15985 this.inKeyMode = true;
15989 "enter" : function(e){
15990 if(this.fireEvent("specialkey", this, e)){
15991 this.onViewClick(false);
15997 "esc" : function(e){
15998 this.onTickableFooterButtonClick(e, false, false);
16001 "tab" : function(e){
16002 this.fireEvent("specialkey", this, e);
16004 this.onTickableFooterButtonClick(e, false, false);
16011 doRelay : function(e, fn, key){
16012 if(this.scope.isExpanded()){
16013 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16022 this.queryDelay = Math.max(this.queryDelay || 10,
16023 this.mode == 'local' ? 10 : 250);
16026 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16028 if(this.typeAhead){
16029 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16032 if(this.editable !== false){
16033 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16036 this.indicator = this.indicatorEl();
16038 if(this.indicator){
16039 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16040 this.indicator.hide();
16045 onDestroy : function(){
16047 this.view.setStore(null);
16048 this.view.el.removeAllListeners();
16049 this.view.el.remove();
16050 this.view.purgeListeners();
16053 this.list.dom.innerHTML = '';
16057 this.store.un('beforeload', this.onBeforeLoad, this);
16058 this.store.un('load', this.onLoad, this);
16059 this.store.un('loadexception', this.onLoadException, this);
16061 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16065 fireKey : function(e){
16066 if(e.isNavKeyPress() && !this.list.isVisible()){
16067 this.fireEvent("specialkey", this, e);
16072 onResize: function(w, h)
16076 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16078 // if(typeof w != 'number'){
16079 // // we do not handle it!?!?
16082 // var tw = this.trigger.getWidth();
16083 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16084 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16086 // this.inputEl().setWidth( this.adjustWidth('input', x));
16088 // //this.trigger.setStyle('left', x+'px');
16090 // if(this.list && this.listWidth === undefined){
16091 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16092 // this.list.setWidth(lw);
16093 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16101 * Allow or prevent the user from directly editing the field text. If false is passed,
16102 * the user will only be able to select from the items defined in the dropdown list. This method
16103 * is the runtime equivalent of setting the 'editable' config option at config time.
16104 * @param {Boolean} value True to allow the user to directly edit the field text
16106 setEditable : function(value){
16107 if(value == this.editable){
16110 this.editable = value;
16112 this.inputEl().dom.setAttribute('readOnly', true);
16113 this.inputEl().on('mousedown', this.onTriggerClick, this);
16114 this.inputEl().addClass('x-combo-noedit');
16116 this.inputEl().dom.setAttribute('readOnly', false);
16117 this.inputEl().un('mousedown', this.onTriggerClick, this);
16118 this.inputEl().removeClass('x-combo-noedit');
16124 onBeforeLoad : function(combo,opts){
16125 if(!this.hasFocus){
16129 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16131 this.restrictHeight();
16132 this.selectedIndex = -1;
16136 onLoad : function(){
16138 this.hasQuery = false;
16140 if(!this.hasFocus){
16144 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16145 this.loading.hide();
16148 if(this.store.getCount() > 0){
16151 this.restrictHeight();
16152 if(this.lastQuery == this.allQuery){
16153 if(this.editable && !this.tickable){
16154 this.inputEl().dom.select();
16158 !this.selectByValue(this.value, true) &&
16161 !this.store.lastOptions ||
16162 typeof(this.store.lastOptions.add) == 'undefined' ||
16163 this.store.lastOptions.add != true
16166 this.select(0, true);
16169 if(this.autoFocus){
16172 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16173 this.taTask.delay(this.typeAheadDelay);
16177 this.onEmptyResults();
16183 onLoadException : function()
16185 this.hasQuery = false;
16187 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16188 this.loading.hide();
16191 if(this.tickable && this.editable){
16196 // only causes errors at present
16197 //Roo.log(this.store.reader.jsonData);
16198 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16200 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16206 onTypeAhead : function(){
16207 if(this.store.getCount() > 0){
16208 var r = this.store.getAt(0);
16209 var newValue = r.data[this.displayField];
16210 var len = newValue.length;
16211 var selStart = this.getRawValue().length;
16213 if(selStart != len){
16214 this.setRawValue(newValue);
16215 this.selectText(selStart, newValue.length);
16221 onSelect : function(record, index){
16223 if(this.fireEvent('beforeselect', this, record, index) !== false){
16225 this.setFromData(index > -1 ? record.data : false);
16228 this.fireEvent('select', this, record, index);
16233 * Returns the currently selected field value or empty string if no value is set.
16234 * @return {String} value The selected value
16236 getValue : function()
16238 if(Roo.isIOS && this.useNativeIOS){
16239 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16243 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16246 if(this.valueField){
16247 return typeof this.value != 'undefined' ? this.value : '';
16249 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16253 getRawValue : function()
16255 if(Roo.isIOS && this.useNativeIOS){
16256 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16259 var v = this.inputEl().getValue();
16265 * Clears any text/value currently set in the field
16267 clearValue : function(){
16269 if(this.hiddenField){
16270 this.hiddenField.dom.value = '';
16273 this.setRawValue('');
16274 this.lastSelectionText = '';
16275 this.lastData = false;
16277 var close = this.closeTriggerEl();
16288 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16289 * will be displayed in the field. If the value does not match the data value of an existing item,
16290 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16291 * Otherwise the field will be blank (although the value will still be set).
16292 * @param {String} value The value to match
16294 setValue : function(v)
16296 if(Roo.isIOS && this.useNativeIOS){
16297 this.setIOSValue(v);
16307 if(this.valueField){
16308 var r = this.findRecord(this.valueField, v);
16310 text = r.data[this.displayField];
16311 }else if(this.valueNotFoundText !== undefined){
16312 text = this.valueNotFoundText;
16315 this.lastSelectionText = text;
16316 if(this.hiddenField){
16317 this.hiddenField.dom.value = v;
16319 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16322 var close = this.closeTriggerEl();
16325 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16331 * @property {Object} the last set data for the element
16336 * Sets the value of the field based on a object which is related to the record format for the store.
16337 * @param {Object} value the value to set as. or false on reset?
16339 setFromData : function(o){
16346 var dv = ''; // display value
16347 var vv = ''; // value value..
16349 if (this.displayField) {
16350 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16352 // this is an error condition!!!
16353 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16356 if(this.valueField){
16357 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16360 var close = this.closeTriggerEl();
16363 if(dv.length || vv * 1 > 0){
16365 this.blockFocus=true;
16371 if(this.hiddenField){
16372 this.hiddenField.dom.value = vv;
16374 this.lastSelectionText = dv;
16375 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16379 // no hidden field.. - we store the value in 'value', but still display
16380 // display field!!!!
16381 this.lastSelectionText = dv;
16382 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16389 reset : function(){
16390 // overridden so that last data is reset..
16397 this.setValue(this.originalValue);
16398 //this.clearInvalid();
16399 this.lastData = false;
16401 this.view.clearSelections();
16407 findRecord : function(prop, value){
16409 if(this.store.getCount() > 0){
16410 this.store.each(function(r){
16411 if(r.data[prop] == value){
16421 getName: function()
16423 // returns hidden if it's set..
16424 if (!this.rendered) {return ''};
16425 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16429 onViewMove : function(e, t){
16430 this.inKeyMode = false;
16434 onViewOver : function(e, t){
16435 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16438 var item = this.view.findItemFromChild(t);
16441 var index = this.view.indexOf(item);
16442 this.select(index, false);
16447 onViewClick : function(view, doFocus, el, e)
16449 var index = this.view.getSelectedIndexes()[0];
16451 var r = this.store.getAt(index);
16455 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16462 Roo.each(this.tickItems, function(v,k){
16464 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16466 _this.tickItems.splice(k, 1);
16468 if(typeof(e) == 'undefined' && view == false){
16469 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16481 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16482 this.tickItems.push(r.data);
16485 if(typeof(e) == 'undefined' && view == false){
16486 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16493 this.onSelect(r, index);
16495 if(doFocus !== false && !this.blockFocus){
16496 this.inputEl().focus();
16501 restrictHeight : function(){
16502 //this.innerList.dom.style.height = '';
16503 //var inner = this.innerList.dom;
16504 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16505 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16506 //this.list.beginUpdate();
16507 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16508 this.list.alignTo(this.inputEl(), this.listAlign);
16509 this.list.alignTo(this.inputEl(), this.listAlign);
16510 //this.list.endUpdate();
16514 onEmptyResults : function(){
16516 if(this.tickable && this.editable){
16517 this.hasFocus = false;
16518 this.restrictHeight();
16526 * Returns true if the dropdown list is expanded, else false.
16528 isExpanded : function(){
16529 return this.list.isVisible();
16533 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16534 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16535 * @param {String} value The data value of the item to select
16536 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16537 * selected item if it is not currently in view (defaults to true)
16538 * @return {Boolean} True if the value matched an item in the list, else false
16540 selectByValue : function(v, scrollIntoView){
16541 if(v !== undefined && v !== null){
16542 var r = this.findRecord(this.valueField || this.displayField, v);
16544 this.select(this.store.indexOf(r), scrollIntoView);
16552 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16553 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16554 * @param {Number} index The zero-based index of the list item to select
16555 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16556 * selected item if it is not currently in view (defaults to true)
16558 select : function(index, scrollIntoView){
16559 this.selectedIndex = index;
16560 this.view.select(index);
16561 if(scrollIntoView !== false){
16562 var el = this.view.getNode(index);
16564 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16567 this.list.scrollChildIntoView(el, false);
16573 selectNext : function(){
16574 var ct = this.store.getCount();
16576 if(this.selectedIndex == -1){
16578 }else if(this.selectedIndex < ct-1){
16579 this.select(this.selectedIndex+1);
16585 selectPrev : function(){
16586 var ct = this.store.getCount();
16588 if(this.selectedIndex == -1){
16590 }else if(this.selectedIndex != 0){
16591 this.select(this.selectedIndex-1);
16597 onKeyUp : function(e){
16598 if(this.editable !== false && !e.isSpecialKey()){
16599 this.lastKey = e.getKey();
16600 this.dqTask.delay(this.queryDelay);
16605 validateBlur : function(){
16606 return !this.list || !this.list.isVisible();
16610 initQuery : function(){
16612 var v = this.getRawValue();
16614 if(this.tickable && this.editable){
16615 v = this.tickableInputEl().getValue();
16622 doForce : function(){
16623 if(this.inputEl().dom.value.length > 0){
16624 this.inputEl().dom.value =
16625 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16631 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16632 * query allowing the query action to be canceled if needed.
16633 * @param {String} query The SQL query to execute
16634 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16635 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16636 * saved in the current store (defaults to false)
16638 doQuery : function(q, forceAll){
16640 if(q === undefined || q === null){
16645 forceAll: forceAll,
16649 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16654 forceAll = qe.forceAll;
16655 if(forceAll === true || (q.length >= this.minChars)){
16657 this.hasQuery = true;
16659 if(this.lastQuery != q || this.alwaysQuery){
16660 this.lastQuery = q;
16661 if(this.mode == 'local'){
16662 this.selectedIndex = -1;
16664 this.store.clearFilter();
16667 if(this.specialFilter){
16668 this.fireEvent('specialfilter', this);
16673 this.store.filter(this.displayField, q);
16676 this.store.fireEvent("datachanged", this.store);
16683 this.store.baseParams[this.queryParam] = q;
16685 var options = {params : this.getParams(q)};
16688 options.add = true;
16689 options.params.start = this.page * this.pageSize;
16692 this.store.load(options);
16695 * this code will make the page width larger, at the beginning, the list not align correctly,
16696 * we should expand the list on onLoad
16697 * so command out it
16702 this.selectedIndex = -1;
16707 this.loadNext = false;
16711 getParams : function(q){
16713 //p[this.queryParam] = q;
16717 p.limit = this.pageSize;
16723 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16725 collapse : function(){
16726 if(!this.isExpanded()){
16732 this.hasFocus = false;
16736 this.cancelBtn.hide();
16737 this.trigger.show();
16740 this.tickableInputEl().dom.value = '';
16741 this.tickableInputEl().blur();
16746 Roo.get(document).un('mousedown', this.collapseIf, this);
16747 Roo.get(document).un('mousewheel', this.collapseIf, this);
16748 if (!this.editable) {
16749 Roo.get(document).un('keydown', this.listKeyPress, this);
16751 this.fireEvent('collapse', this);
16757 collapseIf : function(e){
16758 var in_combo = e.within(this.el);
16759 var in_list = e.within(this.list);
16760 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16762 if (in_combo || in_list || is_list) {
16763 //e.stopPropagation();
16768 this.onTickableFooterButtonClick(e, false, false);
16776 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16778 expand : function(){
16780 if(this.isExpanded() || !this.hasFocus){
16784 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16785 this.list.setWidth(lw);
16791 this.restrictHeight();
16795 this.tickItems = Roo.apply([], this.item);
16798 this.cancelBtn.show();
16799 this.trigger.hide();
16802 this.tickableInputEl().focus();
16807 Roo.get(document).on('mousedown', this.collapseIf, this);
16808 Roo.get(document).on('mousewheel', this.collapseIf, this);
16809 if (!this.editable) {
16810 Roo.get(document).on('keydown', this.listKeyPress, this);
16813 this.fireEvent('expand', this);
16817 // Implements the default empty TriggerField.onTriggerClick function
16818 onTriggerClick : function(e)
16820 Roo.log('trigger click');
16822 if(this.disabled || !this.triggerList){
16827 this.loadNext = false;
16829 if(this.isExpanded()){
16831 if (!this.blockFocus) {
16832 this.inputEl().focus();
16836 this.hasFocus = true;
16837 if(this.triggerAction == 'all') {
16838 this.doQuery(this.allQuery, true);
16840 this.doQuery(this.getRawValue());
16842 if (!this.blockFocus) {
16843 this.inputEl().focus();
16848 onTickableTriggerClick : function(e)
16855 this.loadNext = false;
16856 this.hasFocus = true;
16858 if(this.triggerAction == 'all') {
16859 this.doQuery(this.allQuery, true);
16861 this.doQuery(this.getRawValue());
16865 onSearchFieldClick : function(e)
16867 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16868 this.onTickableFooterButtonClick(e, false, false);
16872 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16877 this.loadNext = false;
16878 this.hasFocus = true;
16880 if(this.triggerAction == 'all') {
16881 this.doQuery(this.allQuery, true);
16883 this.doQuery(this.getRawValue());
16887 listKeyPress : function(e)
16889 //Roo.log('listkeypress');
16890 // scroll to first matching element based on key pres..
16891 if (e.isSpecialKey()) {
16894 var k = String.fromCharCode(e.getKey()).toUpperCase();
16897 var csel = this.view.getSelectedNodes();
16898 var cselitem = false;
16900 var ix = this.view.indexOf(csel[0]);
16901 cselitem = this.store.getAt(ix);
16902 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16908 this.store.each(function(v) {
16910 // start at existing selection.
16911 if (cselitem.id == v.id) {
16917 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16918 match = this.store.indexOf(v);
16924 if (match === false) {
16925 return true; // no more action?
16928 this.view.select(match);
16929 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16930 sn.scrollIntoView(sn.dom.parentNode, false);
16933 onViewScroll : function(e, t){
16935 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){
16939 this.hasQuery = true;
16941 this.loading = this.list.select('.loading', true).first();
16943 if(this.loading === null){
16944 this.list.createChild({
16946 cls: 'loading roo-select2-more-results roo-select2-active',
16947 html: 'Loading more results...'
16950 this.loading = this.list.select('.loading', true).first();
16952 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16954 this.loading.hide();
16957 this.loading.show();
16962 this.loadNext = true;
16964 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16969 addItem : function(o)
16971 var dv = ''; // display value
16973 if (this.displayField) {
16974 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16976 // this is an error condition!!!
16977 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16984 var choice = this.choices.createChild({
16986 cls: 'roo-select2-search-choice',
16995 cls: 'roo-select2-search-choice-close fa fa-times',
17000 }, this.searchField);
17002 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17004 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17012 this.inputEl().dom.value = '';
17017 onRemoveItem : function(e, _self, o)
17019 e.preventDefault();
17021 this.lastItem = Roo.apply([], this.item);
17023 var index = this.item.indexOf(o.data) * 1;
17026 Roo.log('not this item?!');
17030 this.item.splice(index, 1);
17035 this.fireEvent('remove', this, e);
17041 syncValue : function()
17043 if(!this.item.length){
17050 Roo.each(this.item, function(i){
17051 if(_this.valueField){
17052 value.push(i[_this.valueField]);
17059 this.value = value.join(',');
17061 if(this.hiddenField){
17062 this.hiddenField.dom.value = this.value;
17065 this.store.fireEvent("datachanged", this.store);
17070 clearItem : function()
17072 if(!this.multiple){
17078 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17086 if(this.tickable && !Roo.isTouch){
17087 this.view.refresh();
17091 inputEl: function ()
17093 if(Roo.isIOS && this.useNativeIOS){
17094 return this.el.select('select.roo-ios-select', true).first();
17097 if(Roo.isTouch && this.mobileTouchView){
17098 return this.el.select('input.form-control',true).first();
17102 return this.searchField;
17105 return this.el.select('input.form-control',true).first();
17108 onTickableFooterButtonClick : function(e, btn, el)
17110 e.preventDefault();
17112 this.lastItem = Roo.apply([], this.item);
17114 if(btn && btn.name == 'cancel'){
17115 this.tickItems = Roo.apply([], this.item);
17124 Roo.each(this.tickItems, function(o){
17132 validate : function()
17134 if(this.getVisibilityEl().hasClass('hidden')){
17138 var v = this.getRawValue();
17141 v = this.getValue();
17144 if(this.disabled || this.allowBlank || v.length){
17149 this.markInvalid();
17153 tickableInputEl : function()
17155 if(!this.tickable || !this.editable){
17156 return this.inputEl();
17159 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17163 getAutoCreateTouchView : function()
17168 cls: 'form-group' //input-group
17174 type : this.inputType,
17175 cls : 'form-control x-combo-noedit',
17176 autocomplete: 'new-password',
17177 placeholder : this.placeholder || '',
17182 input.name = this.name;
17186 input.cls += ' input-' + this.size;
17189 if (this.disabled) {
17190 input.disabled = true;
17194 cls : 'roo-combobox-wrap',
17201 inputblock.cls += ' input-group';
17203 inputblock.cn.unshift({
17205 cls : 'input-group-addon input-group-prepend input-group-text',
17210 if(this.removable && !this.multiple){
17211 inputblock.cls += ' roo-removable';
17213 inputblock.cn.push({
17216 cls : 'roo-combo-removable-btn close'
17220 if(this.hasFeedback && !this.allowBlank){
17222 inputblock.cls += ' has-feedback';
17224 inputblock.cn.push({
17226 cls: 'glyphicon form-control-feedback'
17233 inputblock.cls += (this.before) ? '' : ' input-group';
17235 inputblock.cn.push({
17237 cls : 'input-group-addon input-group-append input-group-text',
17243 var ibwrap = inputblock;
17248 cls: 'roo-select2-choices',
17252 cls: 'roo-select2-search-field',
17265 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17270 cls: 'form-hidden-field'
17276 if(!this.multiple && this.showToggleBtn){
17282 if (this.caret != false) {
17285 cls: 'fa fa-' + this.caret
17292 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17294 Roo.bootstrap.version == 3 ? caret : '',
17297 cls: 'combobox-clear',
17311 combobox.cls += ' roo-select2-container-multi';
17314 var align = this.labelAlign || this.parentLabelAlign();
17316 if (align ==='left' && this.fieldLabel.length) {
17321 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17322 tooltip : 'This field is required'
17326 cls : 'control-label col-form-label',
17327 html : this.fieldLabel
17331 cls : 'roo-combobox-wrap ',
17338 var labelCfg = cfg.cn[1];
17339 var contentCfg = cfg.cn[2];
17342 if(this.indicatorpos == 'right'){
17347 cls : 'control-label col-form-label',
17351 html : this.fieldLabel
17355 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17356 tooltip : 'This field is required'
17361 cls : "roo-combobox-wrap ",
17369 labelCfg = cfg.cn[0];
17370 contentCfg = cfg.cn[1];
17375 if(this.labelWidth > 12){
17376 labelCfg.style = "width: " + this.labelWidth + 'px';
17379 if(this.labelWidth < 13 && this.labelmd == 0){
17380 this.labelmd = this.labelWidth;
17383 if(this.labellg > 0){
17384 labelCfg.cls += ' col-lg-' + this.labellg;
17385 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17388 if(this.labelmd > 0){
17389 labelCfg.cls += ' col-md-' + this.labelmd;
17390 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17393 if(this.labelsm > 0){
17394 labelCfg.cls += ' col-sm-' + this.labelsm;
17395 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17398 if(this.labelxs > 0){
17399 labelCfg.cls += ' col-xs-' + this.labelxs;
17400 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17404 } else if ( this.fieldLabel.length) {
17408 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17409 tooltip : 'This field is required'
17413 cls : 'control-label',
17414 html : this.fieldLabel
17425 if(this.indicatorpos == 'right'){
17429 cls : 'control-label',
17430 html : this.fieldLabel,
17434 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17435 tooltip : 'This field is required'
17452 var settings = this;
17454 ['xs','sm','md','lg'].map(function(size){
17455 if (settings[size]) {
17456 cfg.cls += ' col-' + size + '-' + settings[size];
17463 initTouchView : function()
17465 this.renderTouchView();
17467 this.touchViewEl.on('scroll', function(){
17468 this.el.dom.scrollTop = 0;
17471 this.originalValue = this.getValue();
17473 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17475 this.inputEl().on("click", this.showTouchView, this);
17476 if (this.triggerEl) {
17477 this.triggerEl.on("click", this.showTouchView, this);
17481 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17482 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17484 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17486 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17487 this.store.on('load', this.onTouchViewLoad, this);
17488 this.store.on('loadexception', this.onTouchViewLoadException, this);
17490 if(this.hiddenName){
17492 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17494 this.hiddenField.dom.value =
17495 this.hiddenValue !== undefined ? this.hiddenValue :
17496 this.value !== undefined ? this.value : '';
17498 this.el.dom.removeAttribute('name');
17499 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17503 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17504 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17507 if(this.removable && !this.multiple){
17508 var close = this.closeTriggerEl();
17510 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17511 close.on('click', this.removeBtnClick, this, close);
17515 * fix the bug in Safari iOS8
17517 this.inputEl().on("focus", function(e){
17518 document.activeElement.blur();
17521 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17528 renderTouchView : function()
17530 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17531 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17533 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17534 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17536 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17537 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17538 this.touchViewBodyEl.setStyle('overflow', 'auto');
17540 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17541 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17543 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17544 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17548 showTouchView : function()
17554 this.touchViewHeaderEl.hide();
17556 if(this.modalTitle.length){
17557 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17558 this.touchViewHeaderEl.show();
17561 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17562 this.touchViewEl.show();
17564 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17566 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17567 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17569 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17571 if(this.modalTitle.length){
17572 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17575 this.touchViewBodyEl.setHeight(bodyHeight);
17579 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17581 this.touchViewEl.addClass(['in','show']);
17584 if(this._touchViewMask){
17585 Roo.get(document.body).addClass("x-body-masked");
17586 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17587 this._touchViewMask.setStyle('z-index', 10000);
17588 this._touchViewMask.addClass('show');
17591 this.doTouchViewQuery();
17595 hideTouchView : function()
17597 this.touchViewEl.removeClass(['in','show']);
17601 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17603 this.touchViewEl.setStyle('display', 'none');
17606 if(this._touchViewMask){
17607 this._touchViewMask.removeClass('show');
17608 Roo.get(document.body).removeClass("x-body-masked");
17612 setTouchViewValue : function()
17619 Roo.each(this.tickItems, function(o){
17624 this.hideTouchView();
17627 doTouchViewQuery : function()
17636 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17640 if(!this.alwaysQuery || this.mode == 'local'){
17641 this.onTouchViewLoad();
17648 onTouchViewBeforeLoad : function(combo,opts)
17654 onTouchViewLoad : function()
17656 if(this.store.getCount() < 1){
17657 this.onTouchViewEmptyResults();
17661 this.clearTouchView();
17663 var rawValue = this.getRawValue();
17665 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17667 this.tickItems = [];
17669 this.store.data.each(function(d, rowIndex){
17670 var row = this.touchViewListGroup.createChild(template);
17672 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17673 row.addClass(d.data.cls);
17676 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17679 html : d.data[this.displayField]
17682 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17683 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17686 row.removeClass('selected');
17687 if(!this.multiple && this.valueField &&
17688 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17691 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17692 row.addClass('selected');
17695 if(this.multiple && this.valueField &&
17696 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17700 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17701 this.tickItems.push(d.data);
17704 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17708 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17710 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17712 if(this.modalTitle.length){
17713 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17716 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17718 if(this.mobile_restrict_height && listHeight < bodyHeight){
17719 this.touchViewBodyEl.setHeight(listHeight);
17724 if(firstChecked && listHeight > bodyHeight){
17725 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17730 onTouchViewLoadException : function()
17732 this.hideTouchView();
17735 onTouchViewEmptyResults : function()
17737 this.clearTouchView();
17739 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17741 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17745 clearTouchView : function()
17747 this.touchViewListGroup.dom.innerHTML = '';
17750 onTouchViewClick : function(e, el, o)
17752 e.preventDefault();
17755 var rowIndex = o.rowIndex;
17757 var r = this.store.getAt(rowIndex);
17759 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17761 if(!this.multiple){
17762 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17763 c.dom.removeAttribute('checked');
17766 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17768 this.setFromData(r.data);
17770 var close = this.closeTriggerEl();
17776 this.hideTouchView();
17778 this.fireEvent('select', this, r, rowIndex);
17783 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17784 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17785 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17789 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17790 this.addItem(r.data);
17791 this.tickItems.push(r.data);
17795 getAutoCreateNativeIOS : function()
17798 cls: 'form-group' //input-group,
17803 cls : 'roo-ios-select'
17807 combobox.name = this.name;
17810 if (this.disabled) {
17811 combobox.disabled = true;
17814 var settings = this;
17816 ['xs','sm','md','lg'].map(function(size){
17817 if (settings[size]) {
17818 cfg.cls += ' col-' + size + '-' + settings[size];
17828 initIOSView : function()
17830 this.store.on('load', this.onIOSViewLoad, this);
17835 onIOSViewLoad : function()
17837 if(this.store.getCount() < 1){
17841 this.clearIOSView();
17843 if(this.allowBlank) {
17845 var default_text = '-- SELECT --';
17847 if(this.placeholder.length){
17848 default_text = this.placeholder;
17851 if(this.emptyTitle.length){
17852 default_text += ' - ' + this.emptyTitle + ' -';
17855 var opt = this.inputEl().createChild({
17858 html : default_text
17862 o[this.valueField] = 0;
17863 o[this.displayField] = default_text;
17865 this.ios_options.push({
17872 this.store.data.each(function(d, rowIndex){
17876 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17877 html = d.data[this.displayField];
17882 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17883 value = d.data[this.valueField];
17892 if(this.value == d.data[this.valueField]){
17893 option['selected'] = true;
17896 var opt = this.inputEl().createChild(option);
17898 this.ios_options.push({
17905 this.inputEl().on('change', function(){
17906 this.fireEvent('select', this);
17911 clearIOSView: function()
17913 this.inputEl().dom.innerHTML = '';
17915 this.ios_options = [];
17918 setIOSValue: function(v)
17922 if(!this.ios_options){
17926 Roo.each(this.ios_options, function(opts){
17928 opts.el.dom.removeAttribute('selected');
17930 if(opts.data[this.valueField] != v){
17934 opts.el.dom.setAttribute('selected', true);
17940 * @cfg {Boolean} grow
17944 * @cfg {Number} growMin
17948 * @cfg {Number} growMax
17957 Roo.apply(Roo.bootstrap.ComboBox, {
17961 cls: 'modal-header',
17983 cls: 'list-group-item',
17987 cls: 'roo-combobox-list-group-item-value'
17991 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18005 listItemCheckbox : {
18007 cls: 'list-group-item',
18011 cls: 'roo-combobox-list-group-item-value'
18015 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18031 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18036 cls: 'modal-footer',
18044 cls: 'col-xs-6 text-left',
18047 cls: 'btn btn-danger roo-touch-view-cancel',
18053 cls: 'col-xs-6 text-right',
18056 cls: 'btn btn-success roo-touch-view-ok',
18067 Roo.apply(Roo.bootstrap.ComboBox, {
18069 touchViewTemplate : {
18071 cls: 'modal fade roo-combobox-touch-view',
18075 cls: 'modal-dialog',
18076 style : 'position:fixed', // we have to fix position....
18080 cls: 'modal-content',
18082 Roo.bootstrap.ComboBox.header,
18083 Roo.bootstrap.ComboBox.body,
18084 Roo.bootstrap.ComboBox.footer
18093 * Ext JS Library 1.1.1
18094 * Copyright(c) 2006-2007, Ext JS, LLC.
18096 * Originally Released Under LGPL - original licence link has changed is not relivant.
18099 * <script type="text/javascript">
18104 * @extends Roo.util.Observable
18105 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18106 * This class also supports single and multi selection modes. <br>
18107 * Create a data model bound view:
18109 var store = new Roo.data.Store(...);
18111 var view = new Roo.View({
18113 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18115 singleSelect: true,
18116 selectedClass: "ydataview-selected",
18120 // listen for node click?
18121 view.on("click", function(vw, index, node, e){
18122 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18126 dataModel.load("foobar.xml");
18128 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18130 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18131 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18133 * Note: old style constructor is still suported (container, template, config)
18136 * Create a new View
18137 * @param {Object} config The config object
18140 Roo.View = function(config, depreciated_tpl, depreciated_config){
18142 this.parent = false;
18144 if (typeof(depreciated_tpl) == 'undefined') {
18145 // new way.. - universal constructor.
18146 Roo.apply(this, config);
18147 this.el = Roo.get(this.el);
18150 this.el = Roo.get(config);
18151 this.tpl = depreciated_tpl;
18152 Roo.apply(this, depreciated_config);
18154 this.wrapEl = this.el.wrap().wrap();
18155 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18158 if(typeof(this.tpl) == "string"){
18159 this.tpl = new Roo.Template(this.tpl);
18161 // support xtype ctors..
18162 this.tpl = new Roo.factory(this.tpl, Roo);
18166 this.tpl.compile();
18171 * @event beforeclick
18172 * Fires before a click is processed. Returns false to cancel the default action.
18173 * @param {Roo.View} this
18174 * @param {Number} index The index of the target node
18175 * @param {HTMLElement} node The target node
18176 * @param {Roo.EventObject} e The raw event object
18178 "beforeclick" : true,
18181 * Fires when a template node is clicked.
18182 * @param {Roo.View} this
18183 * @param {Number} index The index of the target node
18184 * @param {HTMLElement} node The target node
18185 * @param {Roo.EventObject} e The raw event object
18190 * Fires when a template node is double clicked.
18191 * @param {Roo.View} this
18192 * @param {Number} index The index of the target node
18193 * @param {HTMLElement} node The target node
18194 * @param {Roo.EventObject} e The raw event object
18198 * @event contextmenu
18199 * Fires when a template node is right clicked.
18200 * @param {Roo.View} this
18201 * @param {Number} index The index of the target node
18202 * @param {HTMLElement} node The target node
18203 * @param {Roo.EventObject} e The raw event object
18205 "contextmenu" : true,
18207 * @event selectionchange
18208 * Fires when the selected nodes change.
18209 * @param {Roo.View} this
18210 * @param {Array} selections Array of the selected nodes
18212 "selectionchange" : true,
18215 * @event beforeselect
18216 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18217 * @param {Roo.View} this
18218 * @param {HTMLElement} node The node to be selected
18219 * @param {Array} selections Array of currently selected nodes
18221 "beforeselect" : true,
18223 * @event preparedata
18224 * Fires on every row to render, to allow you to change the data.
18225 * @param {Roo.View} this
18226 * @param {Object} data to be rendered (change this)
18228 "preparedata" : true
18236 "click": this.onClick,
18237 "dblclick": this.onDblClick,
18238 "contextmenu": this.onContextMenu,
18242 this.selections = [];
18244 this.cmp = new Roo.CompositeElementLite([]);
18246 this.store = Roo.factory(this.store, Roo.data);
18247 this.setStore(this.store, true);
18250 if ( this.footer && this.footer.xtype) {
18252 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18254 this.footer.dataSource = this.store;
18255 this.footer.container = fctr;
18256 this.footer = Roo.factory(this.footer, Roo);
18257 fctr.insertFirst(this.el);
18259 // this is a bit insane - as the paging toolbar seems to detach the el..
18260 // dom.parentNode.parentNode.parentNode
18261 // they get detached?
18265 Roo.View.superclass.constructor.call(this);
18270 Roo.extend(Roo.View, Roo.util.Observable, {
18273 * @cfg {Roo.data.Store} store Data store to load data from.
18278 * @cfg {String|Roo.Element} el The container element.
18283 * @cfg {String|Roo.Template} tpl The template used by this View
18287 * @cfg {String} dataName the named area of the template to use as the data area
18288 * Works with domtemplates roo-name="name"
18292 * @cfg {String} selectedClass The css class to add to selected nodes
18294 selectedClass : "x-view-selected",
18296 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18301 * @cfg {String} text to display on mask (default Loading)
18305 * @cfg {Boolean} multiSelect Allow multiple selection
18307 multiSelect : false,
18309 * @cfg {Boolean} singleSelect Allow single selection
18311 singleSelect: false,
18314 * @cfg {Boolean} toggleSelect - selecting
18316 toggleSelect : false,
18319 * @cfg {Boolean} tickable - selecting
18324 * Returns the element this view is bound to.
18325 * @return {Roo.Element}
18327 getEl : function(){
18328 return this.wrapEl;
18334 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18336 refresh : function(){
18337 //Roo.log('refresh');
18340 // if we are using something like 'domtemplate', then
18341 // the what gets used is:
18342 // t.applySubtemplate(NAME, data, wrapping data..)
18343 // the outer template then get' applied with
18344 // the store 'extra data'
18345 // and the body get's added to the
18346 // roo-name="data" node?
18347 // <span class='roo-tpl-{name}'></span> ?????
18351 this.clearSelections();
18352 this.el.update("");
18354 var records = this.store.getRange();
18355 if(records.length < 1) {
18357 // is this valid?? = should it render a template??
18359 this.el.update(this.emptyText);
18363 if (this.dataName) {
18364 this.el.update(t.apply(this.store.meta)); //????
18365 el = this.el.child('.roo-tpl-' + this.dataName);
18368 for(var i = 0, len = records.length; i < len; i++){
18369 var data = this.prepareData(records[i].data, i, records[i]);
18370 this.fireEvent("preparedata", this, data, i, records[i]);
18372 var d = Roo.apply({}, data);
18375 Roo.apply(d, {'roo-id' : Roo.id()});
18379 Roo.each(this.parent.item, function(item){
18380 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18383 Roo.apply(d, {'roo-data-checked' : 'checked'});
18387 html[html.length] = Roo.util.Format.trim(
18389 t.applySubtemplate(this.dataName, d, this.store.meta) :
18396 el.update(html.join(""));
18397 this.nodes = el.dom.childNodes;
18398 this.updateIndexes(0);
18403 * Function to override to reformat the data that is sent to
18404 * the template for each node.
18405 * DEPRICATED - use the preparedata event handler.
18406 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18407 * a JSON object for an UpdateManager bound view).
18409 prepareData : function(data, index, record)
18411 this.fireEvent("preparedata", this, data, index, record);
18415 onUpdate : function(ds, record){
18416 // Roo.log('on update');
18417 this.clearSelections();
18418 var index = this.store.indexOf(record);
18419 var n = this.nodes[index];
18420 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18421 n.parentNode.removeChild(n);
18422 this.updateIndexes(index, index);
18428 onAdd : function(ds, records, index)
18430 //Roo.log(['on Add', ds, records, index] );
18431 this.clearSelections();
18432 if(this.nodes.length == 0){
18436 var n = this.nodes[index];
18437 for(var i = 0, len = records.length; i < len; i++){
18438 var d = this.prepareData(records[i].data, i, records[i]);
18440 this.tpl.insertBefore(n, d);
18443 this.tpl.append(this.el, d);
18446 this.updateIndexes(index);
18449 onRemove : function(ds, record, index){
18450 // Roo.log('onRemove');
18451 this.clearSelections();
18452 var el = this.dataName ?
18453 this.el.child('.roo-tpl-' + this.dataName) :
18456 el.dom.removeChild(this.nodes[index]);
18457 this.updateIndexes(index);
18461 * Refresh an individual node.
18462 * @param {Number} index
18464 refreshNode : function(index){
18465 this.onUpdate(this.store, this.store.getAt(index));
18468 updateIndexes : function(startIndex, endIndex){
18469 var ns = this.nodes;
18470 startIndex = startIndex || 0;
18471 endIndex = endIndex || ns.length - 1;
18472 for(var i = startIndex; i <= endIndex; i++){
18473 ns[i].nodeIndex = i;
18478 * Changes the data store this view uses and refresh the view.
18479 * @param {Store} store
18481 setStore : function(store, initial){
18482 if(!initial && this.store){
18483 this.store.un("datachanged", this.refresh);
18484 this.store.un("add", this.onAdd);
18485 this.store.un("remove", this.onRemove);
18486 this.store.un("update", this.onUpdate);
18487 this.store.un("clear", this.refresh);
18488 this.store.un("beforeload", this.onBeforeLoad);
18489 this.store.un("load", this.onLoad);
18490 this.store.un("loadexception", this.onLoad);
18494 store.on("datachanged", this.refresh, this);
18495 store.on("add", this.onAdd, this);
18496 store.on("remove", this.onRemove, this);
18497 store.on("update", this.onUpdate, this);
18498 store.on("clear", this.refresh, this);
18499 store.on("beforeload", this.onBeforeLoad, this);
18500 store.on("load", this.onLoad, this);
18501 store.on("loadexception", this.onLoad, this);
18509 * onbeforeLoad - masks the loading area.
18512 onBeforeLoad : function(store,opts)
18514 //Roo.log('onBeforeLoad');
18516 this.el.update("");
18518 this.el.mask(this.mask ? this.mask : "Loading" );
18520 onLoad : function ()
18527 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18528 * @param {HTMLElement} node
18529 * @return {HTMLElement} The template node
18531 findItemFromChild : function(node){
18532 var el = this.dataName ?
18533 this.el.child('.roo-tpl-' + this.dataName,true) :
18536 if(!node || node.parentNode == el){
18539 var p = node.parentNode;
18540 while(p && p != el){
18541 if(p.parentNode == el){
18550 onClick : function(e){
18551 var item = this.findItemFromChild(e.getTarget());
18553 var index = this.indexOf(item);
18554 if(this.onItemClick(item, index, e) !== false){
18555 this.fireEvent("click", this, index, item, e);
18558 this.clearSelections();
18563 onContextMenu : function(e){
18564 var item = this.findItemFromChild(e.getTarget());
18566 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18571 onDblClick : function(e){
18572 var item = this.findItemFromChild(e.getTarget());
18574 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18578 onItemClick : function(item, index, e)
18580 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18583 if (this.toggleSelect) {
18584 var m = this.isSelected(item) ? 'unselect' : 'select';
18587 _t[m](item, true, false);
18590 if(this.multiSelect || this.singleSelect){
18591 if(this.multiSelect && e.shiftKey && this.lastSelection){
18592 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18594 this.select(item, this.multiSelect && e.ctrlKey);
18595 this.lastSelection = item;
18598 if(!this.tickable){
18599 e.preventDefault();
18607 * Get the number of selected nodes.
18610 getSelectionCount : function(){
18611 return this.selections.length;
18615 * Get the currently selected nodes.
18616 * @return {Array} An array of HTMLElements
18618 getSelectedNodes : function(){
18619 return this.selections;
18623 * Get the indexes of the selected nodes.
18626 getSelectedIndexes : function(){
18627 var indexes = [], s = this.selections;
18628 for(var i = 0, len = s.length; i < len; i++){
18629 indexes.push(s[i].nodeIndex);
18635 * Clear all selections
18636 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18638 clearSelections : function(suppressEvent){
18639 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18640 this.cmp.elements = this.selections;
18641 this.cmp.removeClass(this.selectedClass);
18642 this.selections = [];
18643 if(!suppressEvent){
18644 this.fireEvent("selectionchange", this, this.selections);
18650 * Returns true if the passed node is selected
18651 * @param {HTMLElement/Number} node The node or node index
18652 * @return {Boolean}
18654 isSelected : function(node){
18655 var s = this.selections;
18659 node = this.getNode(node);
18660 return s.indexOf(node) !== -1;
18665 * @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
18666 * @param {Boolean} keepExisting (optional) true to keep existing selections
18667 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18669 select : function(nodeInfo, keepExisting, suppressEvent){
18670 if(nodeInfo instanceof Array){
18672 this.clearSelections(true);
18674 for(var i = 0, len = nodeInfo.length; i < len; i++){
18675 this.select(nodeInfo[i], true, true);
18679 var node = this.getNode(nodeInfo);
18680 if(!node || this.isSelected(node)){
18681 return; // already selected.
18684 this.clearSelections(true);
18687 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18688 Roo.fly(node).addClass(this.selectedClass);
18689 this.selections.push(node);
18690 if(!suppressEvent){
18691 this.fireEvent("selectionchange", this, this.selections);
18699 * @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
18700 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18701 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18703 unselect : function(nodeInfo, keepExisting, suppressEvent)
18705 if(nodeInfo instanceof Array){
18706 Roo.each(this.selections, function(s) {
18707 this.unselect(s, nodeInfo);
18711 var node = this.getNode(nodeInfo);
18712 if(!node || !this.isSelected(node)){
18713 //Roo.log("not selected");
18714 return; // not selected.
18718 Roo.each(this.selections, function(s) {
18720 Roo.fly(node).removeClass(this.selectedClass);
18727 this.selections= ns;
18728 this.fireEvent("selectionchange", this, this.selections);
18732 * Gets a template node.
18733 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18734 * @return {HTMLElement} The node or null if it wasn't found
18736 getNode : function(nodeInfo){
18737 if(typeof nodeInfo == "string"){
18738 return document.getElementById(nodeInfo);
18739 }else if(typeof nodeInfo == "number"){
18740 return this.nodes[nodeInfo];
18746 * Gets a range template nodes.
18747 * @param {Number} startIndex
18748 * @param {Number} endIndex
18749 * @return {Array} An array of nodes
18751 getNodes : function(start, end){
18752 var ns = this.nodes;
18753 start = start || 0;
18754 end = typeof end == "undefined" ? ns.length - 1 : end;
18757 for(var i = start; i <= end; i++){
18761 for(var i = start; i >= end; i--){
18769 * Finds the index of the passed node
18770 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18771 * @return {Number} The index of the node or -1
18773 indexOf : function(node){
18774 node = this.getNode(node);
18775 if(typeof node.nodeIndex == "number"){
18776 return node.nodeIndex;
18778 var ns = this.nodes;
18779 for(var i = 0, len = ns.length; i < len; i++){
18790 * based on jquery fullcalendar
18794 Roo.bootstrap = Roo.bootstrap || {};
18796 * @class Roo.bootstrap.Calendar
18797 * @extends Roo.bootstrap.Component
18798 * Bootstrap Calendar class
18799 * @cfg {Boolean} loadMask (true|false) default false
18800 * @cfg {Object} header generate the user specific header of the calendar, default false
18803 * Create a new Container
18804 * @param {Object} config The config object
18809 Roo.bootstrap.Calendar = function(config){
18810 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18814 * Fires when a date is selected
18815 * @param {DatePicker} this
18816 * @param {Date} date The selected date
18820 * @event monthchange
18821 * Fires when the displayed month changes
18822 * @param {DatePicker} this
18823 * @param {Date} date The selected month
18825 'monthchange': true,
18827 * @event evententer
18828 * Fires when mouse over an event
18829 * @param {Calendar} this
18830 * @param {event} Event
18832 'evententer': true,
18834 * @event eventleave
18835 * Fires when the mouse leaves an
18836 * @param {Calendar} this
18839 'eventleave': true,
18841 * @event eventclick
18842 * Fires when the mouse click an
18843 * @param {Calendar} this
18852 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18855 * @cfg {Number} startDay
18856 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18864 getAutoCreate : function(){
18867 var fc_button = function(name, corner, style, content ) {
18868 return Roo.apply({},{
18870 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18872 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18875 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18886 style : 'width:100%',
18893 cls : 'fc-header-left',
18895 fc_button('prev', 'left', 'arrow', '‹' ),
18896 fc_button('next', 'right', 'arrow', '›' ),
18897 { tag: 'span', cls: 'fc-header-space' },
18898 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18906 cls : 'fc-header-center',
18910 cls: 'fc-header-title',
18913 html : 'month / year'
18921 cls : 'fc-header-right',
18923 /* fc_button('month', 'left', '', 'month' ),
18924 fc_button('week', '', '', 'week' ),
18925 fc_button('day', 'right', '', 'day' )
18937 header = this.header;
18940 var cal_heads = function() {
18942 // fixme - handle this.
18944 for (var i =0; i < Date.dayNames.length; i++) {
18945 var d = Date.dayNames[i];
18948 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18949 html : d.substring(0,3)
18953 ret[0].cls += ' fc-first';
18954 ret[6].cls += ' fc-last';
18957 var cal_cell = function(n) {
18960 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18965 cls: 'fc-day-number',
18969 cls: 'fc-day-content',
18973 style: 'position: relative;' // height: 17px;
18985 var cal_rows = function() {
18988 for (var r = 0; r < 6; r++) {
18995 for (var i =0; i < Date.dayNames.length; i++) {
18996 var d = Date.dayNames[i];
18997 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19000 row.cn[0].cls+=' fc-first';
19001 row.cn[0].cn[0].style = 'min-height:90px';
19002 row.cn[6].cls+=' fc-last';
19006 ret[0].cls += ' fc-first';
19007 ret[4].cls += ' fc-prev-last';
19008 ret[5].cls += ' fc-last';
19015 cls: 'fc-border-separate',
19016 style : 'width:100%',
19024 cls : 'fc-first fc-last',
19042 cls : 'fc-content',
19043 style : "position: relative;",
19046 cls : 'fc-view fc-view-month fc-grid',
19047 style : 'position: relative',
19048 unselectable : 'on',
19051 cls : 'fc-event-container',
19052 style : 'position:absolute;z-index:8;top:0;left:0;'
19070 initEvents : function()
19073 throw "can not find store for calendar";
19079 style: "text-align:center",
19083 style: "background-color:white;width:50%;margin:250 auto",
19087 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19098 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19100 var size = this.el.select('.fc-content', true).first().getSize();
19101 this.maskEl.setSize(size.width, size.height);
19102 this.maskEl.enableDisplayMode("block");
19103 if(!this.loadMask){
19104 this.maskEl.hide();
19107 this.store = Roo.factory(this.store, Roo.data);
19108 this.store.on('load', this.onLoad, this);
19109 this.store.on('beforeload', this.onBeforeLoad, this);
19113 this.cells = this.el.select('.fc-day',true);
19114 //Roo.log(this.cells);
19115 this.textNodes = this.el.query('.fc-day-number');
19116 this.cells.addClassOnOver('fc-state-hover');
19118 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19119 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19120 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19121 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19123 this.on('monthchange', this.onMonthChange, this);
19125 this.update(new Date().clearTime());
19128 resize : function() {
19129 var sz = this.el.getSize();
19131 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19132 this.el.select('.fc-day-content div',true).setHeight(34);
19137 showPrevMonth : function(e){
19138 this.update(this.activeDate.add("mo", -1));
19140 showToday : function(e){
19141 this.update(new Date().clearTime());
19144 showNextMonth : function(e){
19145 this.update(this.activeDate.add("mo", 1));
19149 showPrevYear : function(){
19150 this.update(this.activeDate.add("y", -1));
19154 showNextYear : function(){
19155 this.update(this.activeDate.add("y", 1));
19160 update : function(date)
19162 var vd = this.activeDate;
19163 this.activeDate = date;
19164 // if(vd && this.el){
19165 // var t = date.getTime();
19166 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19167 // Roo.log('using add remove');
19169 // this.fireEvent('monthchange', this, date);
19171 // this.cells.removeClass("fc-state-highlight");
19172 // this.cells.each(function(c){
19173 // if(c.dateValue == t){
19174 // c.addClass("fc-state-highlight");
19175 // setTimeout(function(){
19176 // try{c.dom.firstChild.focus();}catch(e){}
19186 var days = date.getDaysInMonth();
19188 var firstOfMonth = date.getFirstDateOfMonth();
19189 var startingPos = firstOfMonth.getDay()-this.startDay;
19191 if(startingPos < this.startDay){
19195 var pm = date.add(Date.MONTH, -1);
19196 var prevStart = pm.getDaysInMonth()-startingPos;
19198 this.cells = this.el.select('.fc-day',true);
19199 this.textNodes = this.el.query('.fc-day-number');
19200 this.cells.addClassOnOver('fc-state-hover');
19202 var cells = this.cells.elements;
19203 var textEls = this.textNodes;
19205 Roo.each(cells, function(cell){
19206 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19209 days += startingPos;
19211 // convert everything to numbers so it's fast
19212 var day = 86400000;
19213 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19216 //Roo.log(prevStart);
19218 var today = new Date().clearTime().getTime();
19219 var sel = date.clearTime().getTime();
19220 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19221 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19222 var ddMatch = this.disabledDatesRE;
19223 var ddText = this.disabledDatesText;
19224 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19225 var ddaysText = this.disabledDaysText;
19226 var format = this.format;
19228 var setCellClass = function(cal, cell){
19232 //Roo.log('set Cell Class');
19234 var t = d.getTime();
19238 cell.dateValue = t;
19240 cell.className += " fc-today";
19241 cell.className += " fc-state-highlight";
19242 cell.title = cal.todayText;
19245 // disable highlight in other month..
19246 //cell.className += " fc-state-highlight";
19251 cell.className = " fc-state-disabled";
19252 cell.title = cal.minText;
19256 cell.className = " fc-state-disabled";
19257 cell.title = cal.maxText;
19261 if(ddays.indexOf(d.getDay()) != -1){
19262 cell.title = ddaysText;
19263 cell.className = " fc-state-disabled";
19266 if(ddMatch && format){
19267 var fvalue = d.dateFormat(format);
19268 if(ddMatch.test(fvalue)){
19269 cell.title = ddText.replace("%0", fvalue);
19270 cell.className = " fc-state-disabled";
19274 if (!cell.initialClassName) {
19275 cell.initialClassName = cell.dom.className;
19278 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19283 for(; i < startingPos; i++) {
19284 textEls[i].innerHTML = (++prevStart);
19285 d.setDate(d.getDate()+1);
19287 cells[i].className = "fc-past fc-other-month";
19288 setCellClass(this, cells[i]);
19293 for(; i < days; i++){
19294 intDay = i - startingPos + 1;
19295 textEls[i].innerHTML = (intDay);
19296 d.setDate(d.getDate()+1);
19298 cells[i].className = ''; // "x-date-active";
19299 setCellClass(this, cells[i]);
19303 for(; i < 42; i++) {
19304 textEls[i].innerHTML = (++extraDays);
19305 d.setDate(d.getDate()+1);
19307 cells[i].className = "fc-future fc-other-month";
19308 setCellClass(this, cells[i]);
19311 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19313 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19315 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19316 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19318 if(totalRows != 6){
19319 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19320 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19323 this.fireEvent('monthchange', this, date);
19327 if(!this.internalRender){
19328 var main = this.el.dom.firstChild;
19329 var w = main.offsetWidth;
19330 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19331 Roo.fly(main).setWidth(w);
19332 this.internalRender = true;
19333 // opera does not respect the auto grow header center column
19334 // then, after it gets a width opera refuses to recalculate
19335 // without a second pass
19336 if(Roo.isOpera && !this.secondPass){
19337 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19338 this.secondPass = true;
19339 this.update.defer(10, this, [date]);
19346 findCell : function(dt) {
19347 dt = dt.clearTime().getTime();
19349 this.cells.each(function(c){
19350 //Roo.log("check " +c.dateValue + '?=' + dt);
19351 if(c.dateValue == dt){
19361 findCells : function(ev) {
19362 var s = ev.start.clone().clearTime().getTime();
19364 var e= ev.end.clone().clearTime().getTime();
19367 this.cells.each(function(c){
19368 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19370 if(c.dateValue > e){
19373 if(c.dateValue < s){
19382 // findBestRow: function(cells)
19386 // for (var i =0 ; i < cells.length;i++) {
19387 // ret = Math.max(cells[i].rows || 0,ret);
19394 addItem : function(ev)
19396 // look for vertical location slot in
19397 var cells = this.findCells(ev);
19399 // ev.row = this.findBestRow(cells);
19401 // work out the location.
19405 for(var i =0; i < cells.length; i++) {
19407 cells[i].row = cells[0].row;
19410 cells[i].row = cells[i].row + 1;
19420 if (crow.start.getY() == cells[i].getY()) {
19422 crow.end = cells[i];
19439 cells[0].events.push(ev);
19441 this.calevents.push(ev);
19444 clearEvents: function() {
19446 if(!this.calevents){
19450 Roo.each(this.cells.elements, function(c){
19456 Roo.each(this.calevents, function(e) {
19457 Roo.each(e.els, function(el) {
19458 el.un('mouseenter' ,this.onEventEnter, this);
19459 el.un('mouseleave' ,this.onEventLeave, this);
19464 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19470 renderEvents: function()
19474 this.cells.each(function(c) {
19483 if(c.row != c.events.length){
19484 r = 4 - (4 - (c.row - c.events.length));
19487 c.events = ev.slice(0, r);
19488 c.more = ev.slice(r);
19490 if(c.more.length && c.more.length == 1){
19491 c.events.push(c.more.pop());
19494 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19498 this.cells.each(function(c) {
19500 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19503 for (var e = 0; e < c.events.length; e++){
19504 var ev = c.events[e];
19505 var rows = ev.rows;
19507 for(var i = 0; i < rows.length; i++) {
19509 // how many rows should it span..
19512 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19513 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19515 unselectable : "on",
19518 cls: 'fc-event-inner',
19522 // cls: 'fc-event-time',
19523 // html : cells.length > 1 ? '' : ev.time
19527 cls: 'fc-event-title',
19528 html : String.format('{0}', ev.title)
19535 cls: 'ui-resizable-handle ui-resizable-e',
19536 html : '  '
19543 cfg.cls += ' fc-event-start';
19545 if ((i+1) == rows.length) {
19546 cfg.cls += ' fc-event-end';
19549 var ctr = _this.el.select('.fc-event-container',true).first();
19550 var cg = ctr.createChild(cfg);
19552 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19553 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19555 var r = (c.more.length) ? 1 : 0;
19556 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19557 cg.setWidth(ebox.right - sbox.x -2);
19559 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19560 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19561 cg.on('click', _this.onEventClick, _this, ev);
19572 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19573 style : 'position: absolute',
19574 unselectable : "on",
19577 cls: 'fc-event-inner',
19581 cls: 'fc-event-title',
19589 cls: 'ui-resizable-handle ui-resizable-e',
19590 html : '  '
19596 var ctr = _this.el.select('.fc-event-container',true).first();
19597 var cg = ctr.createChild(cfg);
19599 var sbox = c.select('.fc-day-content',true).first().getBox();
19600 var ebox = c.select('.fc-day-content',true).first().getBox();
19602 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19603 cg.setWidth(ebox.right - sbox.x -2);
19605 cg.on('click', _this.onMoreEventClick, _this, c.more);
19615 onEventEnter: function (e, el,event,d) {
19616 this.fireEvent('evententer', this, el, event);
19619 onEventLeave: function (e, el,event,d) {
19620 this.fireEvent('eventleave', this, el, event);
19623 onEventClick: function (e, el,event,d) {
19624 this.fireEvent('eventclick', this, el, event);
19627 onMonthChange: function () {
19631 onMoreEventClick: function(e, el, more)
19635 this.calpopover.placement = 'right';
19636 this.calpopover.setTitle('More');
19638 this.calpopover.setContent('');
19640 var ctr = this.calpopover.el.select('.popover-content', true).first();
19642 Roo.each(more, function(m){
19644 cls : 'fc-event-hori fc-event-draggable',
19647 var cg = ctr.createChild(cfg);
19649 cg.on('click', _this.onEventClick, _this, m);
19652 this.calpopover.show(el);
19657 onLoad: function ()
19659 this.calevents = [];
19662 if(this.store.getCount() > 0){
19663 this.store.data.each(function(d){
19666 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19667 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19668 time : d.data.start_time,
19669 title : d.data.title,
19670 description : d.data.description,
19671 venue : d.data.venue
19676 this.renderEvents();
19678 if(this.calevents.length && this.loadMask){
19679 this.maskEl.hide();
19683 onBeforeLoad: function()
19685 this.clearEvents();
19687 this.maskEl.show();
19701 * @class Roo.bootstrap.Popover
19702 * @extends Roo.bootstrap.Component
19703 * Bootstrap Popover class
19704 * @cfg {String} html contents of the popover (or false to use children..)
19705 * @cfg {String} title of popover (or false to hide)
19706 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19707 * @cfg {String} trigger click || hover (or false to trigger manually)
19708 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19709 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19710 * - if false and it has a 'parent' then it will be automatically added to that element
19711 * - if string - Roo.get will be called
19712 * @cfg {Number} delay - delay before showing
19715 * Create a new Popover
19716 * @param {Object} config The config object
19719 Roo.bootstrap.Popover = function(config){
19720 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19726 * After the popover show
19728 * @param {Roo.bootstrap.Popover} this
19733 * After the popover hide
19735 * @param {Roo.bootstrap.Popover} this
19741 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19746 placement : 'right',
19747 trigger : 'hover', // hover
19753 can_build_overlaid : false,
19755 maskEl : false, // the mask element
19758 alignEl : false, // when show is called with an element - this get's stored.
19760 getChildContainer : function()
19762 return this.contentEl;
19765 getPopoverHeader : function()
19767 this.title = true; // flag not to hide it..
19768 this.headerEl.addClass('p-0');
19769 return this.headerEl
19773 getAutoCreate : function(){
19776 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19777 style: 'display:block',
19783 cls : 'popover-inner ',
19787 cls: 'popover-title popover-header',
19788 html : this.title === false ? '' : this.title
19791 cls : 'popover-content popover-body ' + (this.cls || ''),
19792 html : this.html || ''
19803 * @param {string} the title
19805 setTitle: function(str)
19809 this.headerEl.dom.innerHTML = str;
19814 * @param {string} the body content
19816 setContent: function(str)
19819 if (this.contentEl) {
19820 this.contentEl.dom.innerHTML = str;
19824 // as it get's added to the bottom of the page.
19825 onRender : function(ct, position)
19827 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19832 var cfg = Roo.apply({}, this.getAutoCreate());
19836 cfg.cls += ' ' + this.cls;
19839 cfg.style = this.style;
19841 //Roo.log("adding to ");
19842 this.el = Roo.get(document.body).createChild(cfg, position);
19843 // Roo.log(this.el);
19846 this.contentEl = this.el.select('.popover-content',true).first();
19847 this.headerEl = this.el.select('.popover-title',true).first();
19850 if(typeof(this.items) != 'undefined'){
19851 var items = this.items;
19854 for(var i =0;i < items.length;i++) {
19855 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19859 this.items = nitems;
19861 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19862 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19869 resizeMask : function()
19871 this.maskEl.setSize(
19872 Roo.lib.Dom.getViewWidth(true),
19873 Roo.lib.Dom.getViewHeight(true)
19877 initEvents : function()
19881 Roo.bootstrap.Popover.register(this);
19884 this.arrowEl = this.el.select('.arrow',true).first();
19885 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19886 this.el.enableDisplayMode('block');
19890 if (this.over === false && !this.parent()) {
19893 if (this.triggers === false) {
19898 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19899 var triggers = this.trigger ? this.trigger.split(' ') : [];
19900 Roo.each(triggers, function(trigger) {
19902 if (trigger == 'click') {
19903 on_el.on('click', this.toggle, this);
19904 } else if (trigger != 'manual') {
19905 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19906 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19908 on_el.on(eventIn ,this.enter, this);
19909 on_el.on(eventOut, this.leave, this);
19919 toggle : function () {
19920 this.hoverState == 'in' ? this.leave() : this.enter();
19923 enter : function () {
19925 clearTimeout(this.timeout);
19927 this.hoverState = 'in';
19929 if (!this.delay || !this.delay.show) {
19934 this.timeout = setTimeout(function () {
19935 if (_t.hoverState == 'in') {
19938 }, this.delay.show)
19941 leave : function() {
19942 clearTimeout(this.timeout);
19944 this.hoverState = 'out';
19946 if (!this.delay || !this.delay.hide) {
19951 this.timeout = setTimeout(function () {
19952 if (_t.hoverState == 'out') {
19955 }, this.delay.hide)
19959 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19960 * @param {string} (left|right|top|bottom) position
19962 show : function (on_el, placement)
19964 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19965 on_el = on_el || false; // default to false
19968 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19969 on_el = this.parent().el;
19970 } else if (this.over) {
19971 Roo.get(this.over);
19976 this.alignEl = Roo.get( on_el );
19979 this.render(document.body);
19985 if (this.title === false) {
19986 this.headerEl.hide();
19991 this.el.dom.style.display = 'block';
19994 if (this.alignEl) {
19995 this.updatePosition(this.placement, true);
19998 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19999 var es = this.el.getSize();
20000 var x = Roo.lib.Dom.getViewWidth()/2;
20001 var y = Roo.lib.Dom.getViewHeight()/2;
20002 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20007 //var arrow = this.el.select('.arrow',true).first();
20008 //arrow.set(align[2],
20010 this.el.addClass('in');
20014 this.hoverState = 'in';
20017 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20018 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20019 this.maskEl.dom.style.display = 'block';
20020 this.maskEl.addClass('show');
20022 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20024 this.fireEvent('show', this);
20028 * fire this manually after loading a grid in the table for example
20029 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20030 * @param {Boolean} try and move it if we cant get right position.
20032 updatePosition : function(placement, try_move)
20034 // allow for calling with no parameters
20035 placement = placement ? placement : this.placement;
20036 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20038 this.el.removeClass([
20039 'fade','top','bottom', 'left', 'right','in',
20040 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20042 this.el.addClass(placement + ' bs-popover-' + placement);
20044 if (!this.alignEl ) {
20048 switch (placement) {
20050 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20051 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20052 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20053 //normal display... or moved up/down.
20054 this.el.setXY(offset);
20055 var xy = this.alignEl.getAnchorXY('tr', false);
20057 this.arrowEl.setXY(xy);
20060 // continue through...
20061 return this.updatePosition('left', false);
20065 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20066 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20067 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20068 //normal display... or moved up/down.
20069 this.el.setXY(offset);
20070 var xy = this.alignEl.getAnchorXY('tl', false);
20071 xy[0]-=10;xy[1]+=5; // << fix me
20072 this.arrowEl.setXY(xy);
20076 return this.updatePosition('right', false);
20079 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20080 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20081 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20082 //normal display... or moved up/down.
20083 this.el.setXY(offset);
20084 var xy = this.alignEl.getAnchorXY('t', false);
20085 xy[1]-=10; // << fix me
20086 this.arrowEl.setXY(xy);
20090 return this.updatePosition('bottom', false);
20093 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20094 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20095 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20096 //normal display... or moved up/down.
20097 this.el.setXY(offset);
20098 var xy = this.alignEl.getAnchorXY('b', false);
20099 xy[1]+=2; // << fix me
20100 this.arrowEl.setXY(xy);
20104 return this.updatePosition('top', false);
20115 this.el.setXY([0,0]);
20116 this.el.removeClass('in');
20118 this.hoverState = null;
20119 this.maskEl.hide(); // always..
20120 this.fireEvent('hide', this);
20126 Roo.apply(Roo.bootstrap.Popover, {
20129 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20130 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20131 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20132 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20137 clickHander : false,
20140 onMouseDown : function(e)
20142 if (!e.getTarget(".roo-popover")) {
20150 register : function(popup)
20152 if (!Roo.bootstrap.Popover.clickHandler) {
20153 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20155 // hide other popups.
20157 this.popups.push(popup);
20159 hideAll : function()
20161 this.popups.forEach(function(p) {
20169 * Card header - holder for the card header elements.
20174 * @class Roo.bootstrap.PopoverNav
20175 * @extends Roo.bootstrap.NavGroup
20176 * Bootstrap Popover header navigation class
20178 * Create a new Popover Header Navigation
20179 * @param {Object} config The config object
20182 Roo.bootstrap.PopoverNav = function(config){
20183 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20186 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20189 container_method : 'getPopoverHeader'
20207 * @class Roo.bootstrap.Progress
20208 * @extends Roo.bootstrap.Component
20209 * Bootstrap Progress class
20210 * @cfg {Boolean} striped striped of the progress bar
20211 * @cfg {Boolean} active animated of the progress bar
20215 * Create a new Progress
20216 * @param {Object} config The config object
20219 Roo.bootstrap.Progress = function(config){
20220 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20223 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20228 getAutoCreate : function(){
20236 cfg.cls += ' progress-striped';
20240 cfg.cls += ' active';
20259 * @class Roo.bootstrap.ProgressBar
20260 * @extends Roo.bootstrap.Component
20261 * Bootstrap ProgressBar class
20262 * @cfg {Number} aria_valuenow aria-value now
20263 * @cfg {Number} aria_valuemin aria-value min
20264 * @cfg {Number} aria_valuemax aria-value max
20265 * @cfg {String} label label for the progress bar
20266 * @cfg {String} panel (success | info | warning | danger )
20267 * @cfg {String} role role of the progress bar
20268 * @cfg {String} sr_only text
20272 * Create a new ProgressBar
20273 * @param {Object} config The config object
20276 Roo.bootstrap.ProgressBar = function(config){
20277 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20280 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20284 aria_valuemax : 100,
20290 getAutoCreate : function()
20295 cls: 'progress-bar',
20296 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20308 cfg.role = this.role;
20311 if(this.aria_valuenow){
20312 cfg['aria-valuenow'] = this.aria_valuenow;
20315 if(this.aria_valuemin){
20316 cfg['aria-valuemin'] = this.aria_valuemin;
20319 if(this.aria_valuemax){
20320 cfg['aria-valuemax'] = this.aria_valuemax;
20323 if(this.label && !this.sr_only){
20324 cfg.html = this.label;
20328 cfg.cls += ' progress-bar-' + this.panel;
20334 update : function(aria_valuenow)
20336 this.aria_valuenow = aria_valuenow;
20338 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20353 * @class Roo.bootstrap.TabGroup
20354 * @extends Roo.bootstrap.Column
20355 * Bootstrap Column class
20356 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20357 * @cfg {Boolean} carousel true to make the group behave like a carousel
20358 * @cfg {Boolean} bullets show bullets for the panels
20359 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20360 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20361 * @cfg {Boolean} showarrow (true|false) show arrow default true
20364 * Create a new TabGroup
20365 * @param {Object} config The config object
20368 Roo.bootstrap.TabGroup = function(config){
20369 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20371 this.navId = Roo.id();
20374 Roo.bootstrap.TabGroup.register(this);
20378 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20381 transition : false,
20386 slideOnTouch : false,
20389 getAutoCreate : function()
20391 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20393 cfg.cls += ' tab-content';
20395 if (this.carousel) {
20396 cfg.cls += ' carousel slide';
20399 cls : 'carousel-inner',
20403 if(this.bullets && !Roo.isTouch){
20406 cls : 'carousel-bullets',
20410 if(this.bullets_cls){
20411 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20418 cfg.cn[0].cn.push(bullets);
20421 if(this.showarrow){
20422 cfg.cn[0].cn.push({
20424 class : 'carousel-arrow',
20428 class : 'carousel-prev',
20432 class : 'fa fa-chevron-left'
20438 class : 'carousel-next',
20442 class : 'fa fa-chevron-right'
20455 initEvents: function()
20457 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20458 // this.el.on("touchstart", this.onTouchStart, this);
20461 if(this.autoslide){
20464 this.slideFn = window.setInterval(function() {
20465 _this.showPanelNext();
20469 if(this.showarrow){
20470 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20471 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20477 // onTouchStart : function(e, el, o)
20479 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20483 // this.showPanelNext();
20487 getChildContainer : function()
20489 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20493 * register a Navigation item
20494 * @param {Roo.bootstrap.NavItem} the navitem to add
20496 register : function(item)
20498 this.tabs.push( item);
20499 item.navId = this.navId; // not really needed..
20504 getActivePanel : function()
20507 Roo.each(this.tabs, function(t) {
20517 getPanelByName : function(n)
20520 Roo.each(this.tabs, function(t) {
20521 if (t.tabId == n) {
20529 indexOfPanel : function(p)
20532 Roo.each(this.tabs, function(t,i) {
20533 if (t.tabId == p.tabId) {
20542 * show a specific panel
20543 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20544 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20546 showPanel : function (pan)
20548 if(this.transition || typeof(pan) == 'undefined'){
20549 Roo.log("waiting for the transitionend");
20553 if (typeof(pan) == 'number') {
20554 pan = this.tabs[pan];
20557 if (typeof(pan) == 'string') {
20558 pan = this.getPanelByName(pan);
20561 var cur = this.getActivePanel();
20564 Roo.log('pan or acitve pan is undefined');
20568 if (pan.tabId == this.getActivePanel().tabId) {
20572 if (false === cur.fireEvent('beforedeactivate')) {
20576 if(this.bullets > 0 && !Roo.isTouch){
20577 this.setActiveBullet(this.indexOfPanel(pan));
20580 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20582 //class="carousel-item carousel-item-next carousel-item-left"
20584 this.transition = true;
20585 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20586 var lr = dir == 'next' ? 'left' : 'right';
20587 pan.el.addClass(dir); // or prev
20588 pan.el.addClass('carousel-item-' + dir); // or prev
20589 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20590 cur.el.addClass(lr); // or right
20591 pan.el.addClass(lr);
20592 cur.el.addClass('carousel-item-' +lr); // or right
20593 pan.el.addClass('carousel-item-' +lr);
20597 cur.el.on('transitionend', function() {
20598 Roo.log("trans end?");
20600 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20601 pan.setActive(true);
20603 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20604 cur.setActive(false);
20606 _this.transition = false;
20608 }, this, { single: true } );
20613 cur.setActive(false);
20614 pan.setActive(true);
20619 showPanelNext : function()
20621 var i = this.indexOfPanel(this.getActivePanel());
20623 if (i >= this.tabs.length - 1 && !this.autoslide) {
20627 if (i >= this.tabs.length - 1 && this.autoslide) {
20631 this.showPanel(this.tabs[i+1]);
20634 showPanelPrev : function()
20636 var i = this.indexOfPanel(this.getActivePanel());
20638 if (i < 1 && !this.autoslide) {
20642 if (i < 1 && this.autoslide) {
20643 i = this.tabs.length;
20646 this.showPanel(this.tabs[i-1]);
20650 addBullet: function()
20652 if(!this.bullets || Roo.isTouch){
20655 var ctr = this.el.select('.carousel-bullets',true).first();
20656 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20657 var bullet = ctr.createChild({
20658 cls : 'bullet bullet-' + i
20659 },ctr.dom.lastChild);
20664 bullet.on('click', (function(e, el, o, ii, t){
20666 e.preventDefault();
20668 this.showPanel(ii);
20670 if(this.autoslide && this.slideFn){
20671 clearInterval(this.slideFn);
20672 this.slideFn = window.setInterval(function() {
20673 _this.showPanelNext();
20677 }).createDelegate(this, [i, bullet], true));
20682 setActiveBullet : function(i)
20688 Roo.each(this.el.select('.bullet', true).elements, function(el){
20689 el.removeClass('selected');
20692 var bullet = this.el.select('.bullet-' + i, true).first();
20698 bullet.addClass('selected');
20709 Roo.apply(Roo.bootstrap.TabGroup, {
20713 * register a Navigation Group
20714 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20716 register : function(navgrp)
20718 this.groups[navgrp.navId] = navgrp;
20722 * fetch a Navigation Group based on the navigation ID
20723 * if one does not exist , it will get created.
20724 * @param {string} the navgroup to add
20725 * @returns {Roo.bootstrap.NavGroup} the navgroup
20727 get: function(navId) {
20728 if (typeof(this.groups[navId]) == 'undefined') {
20729 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20731 return this.groups[navId] ;
20746 * @class Roo.bootstrap.TabPanel
20747 * @extends Roo.bootstrap.Component
20748 * Bootstrap TabPanel class
20749 * @cfg {Boolean} active panel active
20750 * @cfg {String} html panel content
20751 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20752 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20753 * @cfg {String} href click to link..
20754 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20758 * Create a new TabPanel
20759 * @param {Object} config The config object
20762 Roo.bootstrap.TabPanel = function(config){
20763 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20767 * Fires when the active status changes
20768 * @param {Roo.bootstrap.TabPanel} this
20769 * @param {Boolean} state the new state
20774 * @event beforedeactivate
20775 * Fires before a tab is de-activated - can be used to do validation on a form.
20776 * @param {Roo.bootstrap.TabPanel} this
20777 * @return {Boolean} false if there is an error
20780 'beforedeactivate': true
20783 this.tabId = this.tabId || Roo.id();
20787 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20794 touchSlide : false,
20795 getAutoCreate : function(){
20800 // item is needed for carousel - not sure if it has any effect otherwise
20801 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20802 html: this.html || ''
20806 cfg.cls += ' active';
20810 cfg.tabId = this.tabId;
20818 initEvents: function()
20820 var p = this.parent();
20822 this.navId = this.navId || p.navId;
20824 if (typeof(this.navId) != 'undefined') {
20825 // not really needed.. but just in case.. parent should be a NavGroup.
20826 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20830 var i = tg.tabs.length - 1;
20832 if(this.active && tg.bullets > 0 && i < tg.bullets){
20833 tg.setActiveBullet(i);
20837 this.el.on('click', this.onClick, this);
20839 if(Roo.isTouch && this.touchSlide){
20840 this.el.on("touchstart", this.onTouchStart, this);
20841 this.el.on("touchmove", this.onTouchMove, this);
20842 this.el.on("touchend", this.onTouchEnd, this);
20847 onRender : function(ct, position)
20849 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20852 setActive : function(state)
20854 Roo.log("panel - set active " + this.tabId + "=" + state);
20856 this.active = state;
20858 this.el.removeClass('active');
20860 } else if (!this.el.hasClass('active')) {
20861 this.el.addClass('active');
20864 this.fireEvent('changed', this, state);
20867 onClick : function(e)
20869 e.preventDefault();
20871 if(!this.href.length){
20875 window.location.href = this.href;
20884 onTouchStart : function(e)
20886 this.swiping = false;
20888 this.startX = e.browserEvent.touches[0].clientX;
20889 this.startY = e.browserEvent.touches[0].clientY;
20892 onTouchMove : function(e)
20894 this.swiping = true;
20896 this.endX = e.browserEvent.touches[0].clientX;
20897 this.endY = e.browserEvent.touches[0].clientY;
20900 onTouchEnd : function(e)
20907 var tabGroup = this.parent();
20909 if(this.endX > this.startX){ // swiping right
20910 tabGroup.showPanelPrev();
20914 if(this.startX > this.endX){ // swiping left
20915 tabGroup.showPanelNext();
20934 * @class Roo.bootstrap.DateField
20935 * @extends Roo.bootstrap.Input
20936 * Bootstrap DateField class
20937 * @cfg {Number} weekStart default 0
20938 * @cfg {String} viewMode default empty, (months|years)
20939 * @cfg {String} minViewMode default empty, (months|years)
20940 * @cfg {Number} startDate default -Infinity
20941 * @cfg {Number} endDate default Infinity
20942 * @cfg {Boolean} todayHighlight default false
20943 * @cfg {Boolean} todayBtn default false
20944 * @cfg {Boolean} calendarWeeks default false
20945 * @cfg {Object} daysOfWeekDisabled default empty
20946 * @cfg {Boolean} singleMode default false (true | false)
20948 * @cfg {Boolean} keyboardNavigation default true
20949 * @cfg {String} language default en
20952 * Create a new DateField
20953 * @param {Object} config The config object
20956 Roo.bootstrap.DateField = function(config){
20957 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20961 * Fires when this field show.
20962 * @param {Roo.bootstrap.DateField} this
20963 * @param {Mixed} date The date value
20968 * Fires when this field hide.
20969 * @param {Roo.bootstrap.DateField} this
20970 * @param {Mixed} date The date value
20975 * Fires when select a date.
20976 * @param {Roo.bootstrap.DateField} this
20977 * @param {Mixed} date The date value
20981 * @event beforeselect
20982 * Fires when before select a date.
20983 * @param {Roo.bootstrap.DateField} this
20984 * @param {Mixed} date The date value
20986 beforeselect : true
20990 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20993 * @cfg {String} format
20994 * The default date format string which can be overriden for localization support. The format must be
20995 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20999 * @cfg {String} altFormats
21000 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21001 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21003 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21011 todayHighlight : false,
21017 keyboardNavigation: true,
21019 calendarWeeks: false,
21021 startDate: -Infinity,
21025 daysOfWeekDisabled: [],
21029 singleMode : false,
21031 UTCDate: function()
21033 return new Date(Date.UTC.apply(Date, arguments));
21036 UTCToday: function()
21038 var today = new Date();
21039 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21042 getDate: function() {
21043 var d = this.getUTCDate();
21044 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21047 getUTCDate: function() {
21051 setDate: function(d) {
21052 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21055 setUTCDate: function(d) {
21057 this.setValue(this.formatDate(this.date));
21060 onRender: function(ct, position)
21063 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21065 this.language = this.language || 'en';
21066 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21067 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21069 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21070 this.format = this.format || 'm/d/y';
21071 this.isInline = false;
21072 this.isInput = true;
21073 this.component = this.el.select('.add-on', true).first() || false;
21074 this.component = (this.component && this.component.length === 0) ? false : this.component;
21075 this.hasInput = this.component && this.inputEl().length;
21077 if (typeof(this.minViewMode === 'string')) {
21078 switch (this.minViewMode) {
21080 this.minViewMode = 1;
21083 this.minViewMode = 2;
21086 this.minViewMode = 0;
21091 if (typeof(this.viewMode === 'string')) {
21092 switch (this.viewMode) {
21105 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21107 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21109 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21111 this.picker().on('mousedown', this.onMousedown, this);
21112 this.picker().on('click', this.onClick, this);
21114 this.picker().addClass('datepicker-dropdown');
21116 this.startViewMode = this.viewMode;
21118 if(this.singleMode){
21119 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21120 v.setVisibilityMode(Roo.Element.DISPLAY);
21124 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21125 v.setStyle('width', '189px');
21129 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21130 if(!this.calendarWeeks){
21135 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21136 v.attr('colspan', function(i, val){
21137 return parseInt(val) + 1;
21142 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21144 this.setStartDate(this.startDate);
21145 this.setEndDate(this.endDate);
21147 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21154 if(this.isInline) {
21159 picker : function()
21161 return this.pickerEl;
21162 // return this.el.select('.datepicker', true).first();
21165 fillDow: function()
21167 var dowCnt = this.weekStart;
21176 if(this.calendarWeeks){
21184 while (dowCnt < this.weekStart + 7) {
21188 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21192 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21195 fillMonths: function()
21198 var months = this.picker().select('>.datepicker-months td', true).first();
21200 months.dom.innerHTML = '';
21206 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21209 months.createChild(month);
21216 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;
21218 if (this.date < this.startDate) {
21219 this.viewDate = new Date(this.startDate);
21220 } else if (this.date > this.endDate) {
21221 this.viewDate = new Date(this.endDate);
21223 this.viewDate = new Date(this.date);
21231 var d = new Date(this.viewDate),
21232 year = d.getUTCFullYear(),
21233 month = d.getUTCMonth(),
21234 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21235 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21236 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21237 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21238 currentDate = this.date && this.date.valueOf(),
21239 today = this.UTCToday();
21241 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21243 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21245 // this.picker.select('>tfoot th.today').
21246 // .text(dates[this.language].today)
21247 // .toggle(this.todayBtn !== false);
21249 this.updateNavArrows();
21252 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21254 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21256 prevMonth.setUTCDate(day);
21258 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21260 var nextMonth = new Date(prevMonth);
21262 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21264 nextMonth = nextMonth.valueOf();
21266 var fillMonths = false;
21268 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21270 while(prevMonth.valueOf() <= nextMonth) {
21273 if (prevMonth.getUTCDay() === this.weekStart) {
21275 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21283 if(this.calendarWeeks){
21284 // ISO 8601: First week contains first thursday.
21285 // ISO also states week starts on Monday, but we can be more abstract here.
21287 // Start of current week: based on weekstart/current date
21288 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21289 // Thursday of this week
21290 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21291 // First Thursday of year, year from thursday
21292 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21293 // Calendar week: ms between thursdays, div ms per day, div 7 days
21294 calWeek = (th - yth) / 864e5 / 7 + 1;
21296 fillMonths.cn.push({
21304 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21306 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21309 if (this.todayHighlight &&
21310 prevMonth.getUTCFullYear() == today.getFullYear() &&
21311 prevMonth.getUTCMonth() == today.getMonth() &&
21312 prevMonth.getUTCDate() == today.getDate()) {
21313 clsName += ' today';
21316 if (currentDate && prevMonth.valueOf() === currentDate) {
21317 clsName += ' active';
21320 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21321 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21322 clsName += ' disabled';
21325 fillMonths.cn.push({
21327 cls: 'day ' + clsName,
21328 html: prevMonth.getDate()
21331 prevMonth.setDate(prevMonth.getDate()+1);
21334 var currentYear = this.date && this.date.getUTCFullYear();
21335 var currentMonth = this.date && this.date.getUTCMonth();
21337 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21339 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21340 v.removeClass('active');
21342 if(currentYear === year && k === currentMonth){
21343 v.addClass('active');
21346 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21347 v.addClass('disabled');
21353 year = parseInt(year/10, 10) * 10;
21355 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21357 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21360 for (var i = -1; i < 11; i++) {
21361 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21363 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21371 showMode: function(dir)
21374 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21377 Roo.each(this.picker().select('>div',true).elements, function(v){
21378 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21381 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21386 if(this.isInline) {
21390 this.picker().removeClass(['bottom', 'top']);
21392 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21394 * place to the top of element!
21398 this.picker().addClass('top');
21399 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21404 this.picker().addClass('bottom');
21406 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21409 parseDate : function(value)
21411 if(!value || value instanceof Date){
21414 var v = Date.parseDate(value, this.format);
21415 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21416 v = Date.parseDate(value, 'Y-m-d');
21418 if(!v && this.altFormats){
21419 if(!this.altFormatsArray){
21420 this.altFormatsArray = this.altFormats.split("|");
21422 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21423 v = Date.parseDate(value, this.altFormatsArray[i]);
21429 formatDate : function(date, fmt)
21431 return (!date || !(date instanceof Date)) ?
21432 date : date.dateFormat(fmt || this.format);
21435 onFocus : function()
21437 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21441 onBlur : function()
21443 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21445 var d = this.inputEl().getValue();
21452 showPopup : function()
21454 this.picker().show();
21458 this.fireEvent('showpopup', this, this.date);
21461 hidePopup : function()
21463 if(this.isInline) {
21466 this.picker().hide();
21467 this.viewMode = this.startViewMode;
21470 this.fireEvent('hidepopup', this, this.date);
21474 onMousedown: function(e)
21476 e.stopPropagation();
21477 e.preventDefault();
21482 Roo.bootstrap.DateField.superclass.keyup.call(this);
21486 setValue: function(v)
21488 if(this.fireEvent('beforeselect', this, v) !== false){
21489 var d = new Date(this.parseDate(v) ).clearTime();
21491 if(isNaN(d.getTime())){
21492 this.date = this.viewDate = '';
21493 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21497 v = this.formatDate(d);
21499 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21501 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21505 this.fireEvent('select', this, this.date);
21509 getValue: function()
21511 return this.formatDate(this.date);
21514 fireKey: function(e)
21516 if (!this.picker().isVisible()){
21517 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21523 var dateChanged = false,
21525 newDate, newViewDate;
21530 e.preventDefault();
21534 if (!this.keyboardNavigation) {
21537 dir = e.keyCode == 37 ? -1 : 1;
21540 newDate = this.moveYear(this.date, dir);
21541 newViewDate = this.moveYear(this.viewDate, dir);
21542 } else if (e.shiftKey){
21543 newDate = this.moveMonth(this.date, dir);
21544 newViewDate = this.moveMonth(this.viewDate, dir);
21546 newDate = new Date(this.date);
21547 newDate.setUTCDate(this.date.getUTCDate() + dir);
21548 newViewDate = new Date(this.viewDate);
21549 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21551 if (this.dateWithinRange(newDate)){
21552 this.date = newDate;
21553 this.viewDate = newViewDate;
21554 this.setValue(this.formatDate(this.date));
21556 e.preventDefault();
21557 dateChanged = true;
21562 if (!this.keyboardNavigation) {
21565 dir = e.keyCode == 38 ? -1 : 1;
21567 newDate = this.moveYear(this.date, dir);
21568 newViewDate = this.moveYear(this.viewDate, dir);
21569 } else if (e.shiftKey){
21570 newDate = this.moveMonth(this.date, dir);
21571 newViewDate = this.moveMonth(this.viewDate, dir);
21573 newDate = new Date(this.date);
21574 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21575 newViewDate = new Date(this.viewDate);
21576 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21578 if (this.dateWithinRange(newDate)){
21579 this.date = newDate;
21580 this.viewDate = newViewDate;
21581 this.setValue(this.formatDate(this.date));
21583 e.preventDefault();
21584 dateChanged = true;
21588 this.setValue(this.formatDate(this.date));
21590 e.preventDefault();
21593 this.setValue(this.formatDate(this.date));
21607 onClick: function(e)
21609 e.stopPropagation();
21610 e.preventDefault();
21612 var target = e.getTarget();
21614 if(target.nodeName.toLowerCase() === 'i'){
21615 target = Roo.get(target).dom.parentNode;
21618 var nodeName = target.nodeName;
21619 var className = target.className;
21620 var html = target.innerHTML;
21621 //Roo.log(nodeName);
21623 switch(nodeName.toLowerCase()) {
21625 switch(className) {
21631 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21632 switch(this.viewMode){
21634 this.viewDate = this.moveMonth(this.viewDate, dir);
21638 this.viewDate = this.moveYear(this.viewDate, dir);
21644 var date = new Date();
21645 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21647 this.setValue(this.formatDate(this.date));
21654 if (className.indexOf('disabled') < 0) {
21655 this.viewDate.setUTCDate(1);
21656 if (className.indexOf('month') > -1) {
21657 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21659 var year = parseInt(html, 10) || 0;
21660 this.viewDate.setUTCFullYear(year);
21664 if(this.singleMode){
21665 this.setValue(this.formatDate(this.viewDate));
21676 //Roo.log(className);
21677 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21678 var day = parseInt(html, 10) || 1;
21679 var year = (this.viewDate || new Date()).getUTCFullYear(),
21680 month = (this.viewDate || new Date()).getUTCMonth();
21682 if (className.indexOf('old') > -1) {
21689 } else if (className.indexOf('new') > -1) {
21697 //Roo.log([year,month,day]);
21698 this.date = this.UTCDate(year, month, day,0,0,0,0);
21699 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21701 //Roo.log(this.formatDate(this.date));
21702 this.setValue(this.formatDate(this.date));
21709 setStartDate: function(startDate)
21711 this.startDate = startDate || -Infinity;
21712 if (this.startDate !== -Infinity) {
21713 this.startDate = this.parseDate(this.startDate);
21716 this.updateNavArrows();
21719 setEndDate: function(endDate)
21721 this.endDate = endDate || Infinity;
21722 if (this.endDate !== Infinity) {
21723 this.endDate = this.parseDate(this.endDate);
21726 this.updateNavArrows();
21729 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21731 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21732 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21733 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21735 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21736 return parseInt(d, 10);
21739 this.updateNavArrows();
21742 updateNavArrows: function()
21744 if(this.singleMode){
21748 var d = new Date(this.viewDate),
21749 year = d.getUTCFullYear(),
21750 month = d.getUTCMonth();
21752 Roo.each(this.picker().select('.prev', true).elements, function(v){
21754 switch (this.viewMode) {
21757 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21763 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21770 Roo.each(this.picker().select('.next', true).elements, function(v){
21772 switch (this.viewMode) {
21775 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21781 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21789 moveMonth: function(date, dir)
21794 var new_date = new Date(date.valueOf()),
21795 day = new_date.getUTCDate(),
21796 month = new_date.getUTCMonth(),
21797 mag = Math.abs(dir),
21799 dir = dir > 0 ? 1 : -1;
21802 // If going back one month, make sure month is not current month
21803 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21805 return new_date.getUTCMonth() == month;
21807 // If going forward one month, make sure month is as expected
21808 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21810 return new_date.getUTCMonth() != new_month;
21812 new_month = month + dir;
21813 new_date.setUTCMonth(new_month);
21814 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21815 if (new_month < 0 || new_month > 11) {
21816 new_month = (new_month + 12) % 12;
21819 // For magnitudes >1, move one month at a time...
21820 for (var i=0; i<mag; i++) {
21821 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21822 new_date = this.moveMonth(new_date, dir);
21824 // ...then reset the day, keeping it in the new month
21825 new_month = new_date.getUTCMonth();
21826 new_date.setUTCDate(day);
21828 return new_month != new_date.getUTCMonth();
21831 // Common date-resetting loop -- if date is beyond end of month, make it
21834 new_date.setUTCDate(--day);
21835 new_date.setUTCMonth(new_month);
21840 moveYear: function(date, dir)
21842 return this.moveMonth(date, dir*12);
21845 dateWithinRange: function(date)
21847 return date >= this.startDate && date <= this.endDate;
21853 this.picker().remove();
21856 validateValue : function(value)
21858 if(this.getVisibilityEl().hasClass('hidden')){
21862 if(value.length < 1) {
21863 if(this.allowBlank){
21869 if(value.length < this.minLength){
21872 if(value.length > this.maxLength){
21876 var vt = Roo.form.VTypes;
21877 if(!vt[this.vtype](value, this)){
21881 if(typeof this.validator == "function"){
21882 var msg = this.validator(value);
21888 if(this.regex && !this.regex.test(value)){
21892 if(typeof(this.parseDate(value)) == 'undefined'){
21896 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21900 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21910 this.date = this.viewDate = '';
21912 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21917 Roo.apply(Roo.bootstrap.DateField, {
21928 html: '<i class="fa fa-arrow-left"/>'
21938 html: '<i class="fa fa-arrow-right"/>'
21980 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21981 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21982 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21983 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21984 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21997 navFnc: 'FullYear',
22002 navFnc: 'FullYear',
22007 Roo.apply(Roo.bootstrap.DateField, {
22011 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22015 cls: 'datepicker-days',
22019 cls: 'table-condensed',
22021 Roo.bootstrap.DateField.head,
22025 Roo.bootstrap.DateField.footer
22032 cls: 'datepicker-months',
22036 cls: 'table-condensed',
22038 Roo.bootstrap.DateField.head,
22039 Roo.bootstrap.DateField.content,
22040 Roo.bootstrap.DateField.footer
22047 cls: 'datepicker-years',
22051 cls: 'table-condensed',
22053 Roo.bootstrap.DateField.head,
22054 Roo.bootstrap.DateField.content,
22055 Roo.bootstrap.DateField.footer
22074 * @class Roo.bootstrap.TimeField
22075 * @extends Roo.bootstrap.Input
22076 * Bootstrap DateField class
22080 * Create a new TimeField
22081 * @param {Object} config The config object
22084 Roo.bootstrap.TimeField = function(config){
22085 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22089 * Fires when this field show.
22090 * @param {Roo.bootstrap.DateField} thisthis
22091 * @param {Mixed} date The date value
22096 * Fires when this field hide.
22097 * @param {Roo.bootstrap.DateField} this
22098 * @param {Mixed} date The date value
22103 * Fires when select a date.
22104 * @param {Roo.bootstrap.DateField} this
22105 * @param {Mixed} date The date value
22111 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22114 * @cfg {String} format
22115 * The default time format string which can be overriden for localization support. The format must be
22116 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22120 getAutoCreate : function()
22122 this.after = '<i class="fa far fa-clock"></i>';
22123 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22127 onRender: function(ct, position)
22130 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22132 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22134 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22136 this.pop = this.picker().select('>.datepicker-time',true).first();
22137 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22139 this.picker().on('mousedown', this.onMousedown, this);
22140 this.picker().on('click', this.onClick, this);
22142 this.picker().addClass('datepicker-dropdown');
22147 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22148 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22149 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22150 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22151 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22152 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22156 fireKey: function(e){
22157 if (!this.picker().isVisible()){
22158 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22164 e.preventDefault();
22172 this.onTogglePeriod();
22175 this.onIncrementMinutes();
22178 this.onDecrementMinutes();
22187 onClick: function(e) {
22188 e.stopPropagation();
22189 e.preventDefault();
22192 picker : function()
22194 return this.pickerEl;
22197 fillTime: function()
22199 var time = this.pop.select('tbody', true).first();
22201 time.dom.innerHTML = '';
22216 cls: 'hours-up fa fas fa-chevron-up'
22236 cls: 'minutes-up fa fas fa-chevron-up'
22257 cls: 'timepicker-hour',
22272 cls: 'timepicker-minute',
22287 cls: 'btn btn-primary period',
22309 cls: 'hours-down fa fas fa-chevron-down'
22329 cls: 'minutes-down fa fas fa-chevron-down'
22347 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22354 var hours = this.time.getHours();
22355 var minutes = this.time.getMinutes();
22368 hours = hours - 12;
22372 hours = '0' + hours;
22376 minutes = '0' + minutes;
22379 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22380 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22381 this.pop.select('button', true).first().dom.innerHTML = period;
22387 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22389 var cls = ['bottom'];
22391 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22398 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22402 //this.picker().setXY(20000,20000);
22403 this.picker().addClass(cls.join('-'));
22407 Roo.each(cls, function(c){
22412 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22413 //_this.picker().setTop(_this.inputEl().getHeight());
22417 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22419 //_this.picker().setTop(0 - _this.picker().getHeight());
22424 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22428 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22436 onFocus : function()
22438 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22442 onBlur : function()
22444 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22450 this.picker().show();
22455 this.fireEvent('show', this, this.date);
22460 this.picker().hide();
22463 this.fireEvent('hide', this, this.date);
22466 setTime : function()
22469 this.setValue(this.time.format(this.format));
22471 this.fireEvent('select', this, this.date);
22476 onMousedown: function(e){
22477 e.stopPropagation();
22478 e.preventDefault();
22481 onIncrementHours: function()
22483 Roo.log('onIncrementHours');
22484 this.time = this.time.add(Date.HOUR, 1);
22489 onDecrementHours: function()
22491 Roo.log('onDecrementHours');
22492 this.time = this.time.add(Date.HOUR, -1);
22496 onIncrementMinutes: function()
22498 Roo.log('onIncrementMinutes');
22499 this.time = this.time.add(Date.MINUTE, 1);
22503 onDecrementMinutes: function()
22505 Roo.log('onDecrementMinutes');
22506 this.time = this.time.add(Date.MINUTE, -1);
22510 onTogglePeriod: function()
22512 Roo.log('onTogglePeriod');
22513 this.time = this.time.add(Date.HOUR, 12);
22521 Roo.apply(Roo.bootstrap.TimeField, {
22525 cls: 'datepicker dropdown-menu',
22529 cls: 'datepicker-time',
22533 cls: 'table-condensed',
22562 cls: 'btn btn-info ok',
22590 * @class Roo.bootstrap.MonthField
22591 * @extends Roo.bootstrap.Input
22592 * Bootstrap MonthField class
22594 * @cfg {String} language default en
22597 * Create a new MonthField
22598 * @param {Object} config The config object
22601 Roo.bootstrap.MonthField = function(config){
22602 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22607 * Fires when this field show.
22608 * @param {Roo.bootstrap.MonthField} this
22609 * @param {Mixed} date The date value
22614 * Fires when this field hide.
22615 * @param {Roo.bootstrap.MonthField} this
22616 * @param {Mixed} date The date value
22621 * Fires when select a date.
22622 * @param {Roo.bootstrap.MonthField} this
22623 * @param {String} oldvalue The old value
22624 * @param {String} newvalue The new value
22630 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22632 onRender: function(ct, position)
22635 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22637 this.language = this.language || 'en';
22638 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22639 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22641 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22642 this.isInline = false;
22643 this.isInput = true;
22644 this.component = this.el.select('.add-on', true).first() || false;
22645 this.component = (this.component && this.component.length === 0) ? false : this.component;
22646 this.hasInput = this.component && this.inputEL().length;
22648 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22650 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22652 this.picker().on('mousedown', this.onMousedown, this);
22653 this.picker().on('click', this.onClick, this);
22655 this.picker().addClass('datepicker-dropdown');
22657 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22658 v.setStyle('width', '189px');
22665 if(this.isInline) {
22671 setValue: function(v, suppressEvent)
22673 var o = this.getValue();
22675 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22679 if(suppressEvent !== true){
22680 this.fireEvent('select', this, o, v);
22685 getValue: function()
22690 onClick: function(e)
22692 e.stopPropagation();
22693 e.preventDefault();
22695 var target = e.getTarget();
22697 if(target.nodeName.toLowerCase() === 'i'){
22698 target = Roo.get(target).dom.parentNode;
22701 var nodeName = target.nodeName;
22702 var className = target.className;
22703 var html = target.innerHTML;
22705 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22709 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22711 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22717 picker : function()
22719 return this.pickerEl;
22722 fillMonths: function()
22725 var months = this.picker().select('>.datepicker-months td', true).first();
22727 months.dom.innerHTML = '';
22733 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22736 months.createChild(month);
22745 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22746 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22749 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22750 e.removeClass('active');
22752 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22753 e.addClass('active');
22760 if(this.isInline) {
22764 this.picker().removeClass(['bottom', 'top']);
22766 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22768 * place to the top of element!
22772 this.picker().addClass('top');
22773 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22778 this.picker().addClass('bottom');
22780 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22783 onFocus : function()
22785 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22789 onBlur : function()
22791 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22793 var d = this.inputEl().getValue();
22802 this.picker().show();
22803 this.picker().select('>.datepicker-months', true).first().show();
22807 this.fireEvent('show', this, this.date);
22812 if(this.isInline) {
22815 this.picker().hide();
22816 this.fireEvent('hide', this, this.date);
22820 onMousedown: function(e)
22822 e.stopPropagation();
22823 e.preventDefault();
22828 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22832 fireKey: function(e)
22834 if (!this.picker().isVisible()){
22835 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22846 e.preventDefault();
22850 dir = e.keyCode == 37 ? -1 : 1;
22852 this.vIndex = this.vIndex + dir;
22854 if(this.vIndex < 0){
22858 if(this.vIndex > 11){
22862 if(isNaN(this.vIndex)){
22866 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22872 dir = e.keyCode == 38 ? -1 : 1;
22874 this.vIndex = this.vIndex + dir * 4;
22876 if(this.vIndex < 0){
22880 if(this.vIndex > 11){
22884 if(isNaN(this.vIndex)){
22888 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22893 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22894 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22898 e.preventDefault();
22901 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22902 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22918 this.picker().remove();
22923 Roo.apply(Roo.bootstrap.MonthField, {
22942 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22943 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22948 Roo.apply(Roo.bootstrap.MonthField, {
22952 cls: 'datepicker dropdown-menu roo-dynamic',
22956 cls: 'datepicker-months',
22960 cls: 'table-condensed',
22962 Roo.bootstrap.DateField.content
22982 * @class Roo.bootstrap.CheckBox
22983 * @extends Roo.bootstrap.Input
22984 * Bootstrap CheckBox class
22986 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22987 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22988 * @cfg {String} boxLabel The text that appears beside the checkbox
22989 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22990 * @cfg {Boolean} checked initnal the element
22991 * @cfg {Boolean} inline inline the element (default false)
22992 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22993 * @cfg {String} tooltip label tooltip
22996 * Create a new CheckBox
22997 * @param {Object} config The config object
23000 Roo.bootstrap.CheckBox = function(config){
23001 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23006 * Fires when the element is checked or unchecked.
23007 * @param {Roo.bootstrap.CheckBox} this This input
23008 * @param {Boolean} checked The new checked value
23013 * Fires when the element is click.
23014 * @param {Roo.bootstrap.CheckBox} this This input
23021 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23023 inputType: 'checkbox',
23032 // checkbox success does not make any sense really..
23037 getAutoCreate : function()
23039 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23045 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23048 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23054 type : this.inputType,
23055 value : this.inputValue,
23056 cls : 'roo-' + this.inputType, //'form-box',
23057 placeholder : this.placeholder || ''
23061 if(this.inputType != 'radio'){
23065 cls : 'roo-hidden-value',
23066 value : this.checked ? this.inputValue : this.valueOff
23071 if (this.weight) { // Validity check?
23072 cfg.cls += " " + this.inputType + "-" + this.weight;
23075 if (this.disabled) {
23076 input.disabled=true;
23080 input.checked = this.checked;
23085 input.name = this.name;
23087 if(this.inputType != 'radio'){
23088 hidden.name = this.name;
23089 input.name = '_hidden_' + this.name;
23094 input.cls += ' input-' + this.size;
23099 ['xs','sm','md','lg'].map(function(size){
23100 if (settings[size]) {
23101 cfg.cls += ' col-' + size + '-' + settings[size];
23105 var inputblock = input;
23107 if (this.before || this.after) {
23110 cls : 'input-group',
23115 inputblock.cn.push({
23117 cls : 'input-group-addon',
23122 inputblock.cn.push(input);
23124 if(this.inputType != 'radio'){
23125 inputblock.cn.push(hidden);
23129 inputblock.cn.push({
23131 cls : 'input-group-addon',
23137 var boxLabelCfg = false;
23143 //'for': id, // box label is handled by onclick - so no for...
23145 html: this.boxLabel
23148 boxLabelCfg.tooltip = this.tooltip;
23154 if (align ==='left' && this.fieldLabel.length) {
23155 // Roo.log("left and has label");
23160 cls : 'control-label',
23161 html : this.fieldLabel
23172 cfg.cn[1].cn.push(boxLabelCfg);
23175 if(this.labelWidth > 12){
23176 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23179 if(this.labelWidth < 13 && this.labelmd == 0){
23180 this.labelmd = this.labelWidth;
23183 if(this.labellg > 0){
23184 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23185 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23188 if(this.labelmd > 0){
23189 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23190 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23193 if(this.labelsm > 0){
23194 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23195 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23198 if(this.labelxs > 0){
23199 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23200 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23203 } else if ( this.fieldLabel.length) {
23204 // Roo.log(" label");
23208 tag: this.boxLabel ? 'span' : 'label',
23210 cls: 'control-label box-input-label',
23211 //cls : 'input-group-addon',
23212 html : this.fieldLabel
23219 cfg.cn.push(boxLabelCfg);
23224 // Roo.log(" no label && no align");
23225 cfg.cn = [ inputblock ] ;
23227 cfg.cn.push(boxLabelCfg);
23235 if(this.inputType != 'radio'){
23236 cfg.cn.push(hidden);
23244 * return the real input element.
23246 inputEl: function ()
23248 return this.el.select('input.roo-' + this.inputType,true).first();
23250 hiddenEl: function ()
23252 return this.el.select('input.roo-hidden-value',true).first();
23255 labelEl: function()
23257 return this.el.select('label.control-label',true).first();
23259 /* depricated... */
23263 return this.labelEl();
23266 boxLabelEl: function()
23268 return this.el.select('label.box-label',true).first();
23271 initEvents : function()
23273 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23275 this.inputEl().on('click', this.onClick, this);
23277 if (this.boxLabel) {
23278 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23281 this.startValue = this.getValue();
23284 Roo.bootstrap.CheckBox.register(this);
23288 onClick : function(e)
23290 if(this.fireEvent('click', this, e) !== false){
23291 this.setChecked(!this.checked);
23296 setChecked : function(state,suppressEvent)
23298 this.startValue = this.getValue();
23300 if(this.inputType == 'radio'){
23302 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23303 e.dom.checked = false;
23306 this.inputEl().dom.checked = true;
23308 this.inputEl().dom.value = this.inputValue;
23310 if(suppressEvent !== true){
23311 this.fireEvent('check', this, true);
23319 this.checked = state;
23321 this.inputEl().dom.checked = state;
23324 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23326 if(suppressEvent !== true){
23327 this.fireEvent('check', this, state);
23333 getValue : function()
23335 if(this.inputType == 'radio'){
23336 return this.getGroupValue();
23339 return this.hiddenEl().dom.value;
23343 getGroupValue : function()
23345 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23349 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23352 setValue : function(v,suppressEvent)
23354 if(this.inputType == 'radio'){
23355 this.setGroupValue(v, suppressEvent);
23359 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23364 setGroupValue : function(v, suppressEvent)
23366 this.startValue = this.getValue();
23368 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23369 e.dom.checked = false;
23371 if(e.dom.value == v){
23372 e.dom.checked = true;
23376 if(suppressEvent !== true){
23377 this.fireEvent('check', this, true);
23385 validate : function()
23387 if(this.getVisibilityEl().hasClass('hidden')){
23393 (this.inputType == 'radio' && this.validateRadio()) ||
23394 (this.inputType == 'checkbox' && this.validateCheckbox())
23400 this.markInvalid();
23404 validateRadio : function()
23406 if(this.getVisibilityEl().hasClass('hidden')){
23410 if(this.allowBlank){
23416 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23417 if(!e.dom.checked){
23429 validateCheckbox : function()
23432 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23433 //return (this.getValue() == this.inputValue) ? true : false;
23436 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23444 for(var i in group){
23445 if(group[i].el.isVisible(true)){
23453 for(var i in group){
23458 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23465 * Mark this field as valid
23467 markValid : function()
23471 this.fireEvent('valid', this);
23473 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23476 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23483 if(this.inputType == 'radio'){
23484 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23485 var fg = e.findParent('.form-group', false, true);
23486 if (Roo.bootstrap.version == 3) {
23487 fg.removeClass([_this.invalidClass, _this.validClass]);
23488 fg.addClass(_this.validClass);
23490 fg.removeClass(['is-valid', 'is-invalid']);
23491 fg.addClass('is-valid');
23499 var fg = this.el.findParent('.form-group', false, true);
23500 if (Roo.bootstrap.version == 3) {
23501 fg.removeClass([this.invalidClass, this.validClass]);
23502 fg.addClass(this.validClass);
23504 fg.removeClass(['is-valid', 'is-invalid']);
23505 fg.addClass('is-valid');
23510 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23516 for(var i in group){
23517 var fg = group[i].el.findParent('.form-group', false, true);
23518 if (Roo.bootstrap.version == 3) {
23519 fg.removeClass([this.invalidClass, this.validClass]);
23520 fg.addClass(this.validClass);
23522 fg.removeClass(['is-valid', 'is-invalid']);
23523 fg.addClass('is-valid');
23529 * Mark this field as invalid
23530 * @param {String} msg The validation message
23532 markInvalid : function(msg)
23534 if(this.allowBlank){
23540 this.fireEvent('invalid', this, msg);
23542 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23545 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23549 label.markInvalid();
23552 if(this.inputType == 'radio'){
23554 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23555 var fg = e.findParent('.form-group', false, true);
23556 if (Roo.bootstrap.version == 3) {
23557 fg.removeClass([_this.invalidClass, _this.validClass]);
23558 fg.addClass(_this.invalidClass);
23560 fg.removeClass(['is-invalid', 'is-valid']);
23561 fg.addClass('is-invalid');
23569 var fg = this.el.findParent('.form-group', false, true);
23570 if (Roo.bootstrap.version == 3) {
23571 fg.removeClass([_this.invalidClass, _this.validClass]);
23572 fg.addClass(_this.invalidClass);
23574 fg.removeClass(['is-invalid', 'is-valid']);
23575 fg.addClass('is-invalid');
23580 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23586 for(var i in group){
23587 var fg = group[i].el.findParent('.form-group', false, true);
23588 if (Roo.bootstrap.version == 3) {
23589 fg.removeClass([_this.invalidClass, _this.validClass]);
23590 fg.addClass(_this.invalidClass);
23592 fg.removeClass(['is-invalid', 'is-valid']);
23593 fg.addClass('is-invalid');
23599 clearInvalid : function()
23601 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23603 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23605 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23607 if (label && label.iconEl) {
23608 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23609 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23613 disable : function()
23615 if(this.inputType != 'radio'){
23616 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23623 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23624 _this.getActionEl().addClass(this.disabledClass);
23625 e.dom.disabled = true;
23629 this.disabled = true;
23630 this.fireEvent("disable", this);
23634 enable : function()
23636 if(this.inputType != 'radio'){
23637 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23644 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23645 _this.getActionEl().removeClass(this.disabledClass);
23646 e.dom.disabled = false;
23650 this.disabled = false;
23651 this.fireEvent("enable", this);
23655 setBoxLabel : function(v)
23660 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23666 Roo.apply(Roo.bootstrap.CheckBox, {
23671 * register a CheckBox Group
23672 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23674 register : function(checkbox)
23676 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23677 this.groups[checkbox.groupId] = {};
23680 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23684 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23688 * fetch a CheckBox Group based on the group ID
23689 * @param {string} the group ID
23690 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23692 get: function(groupId) {
23693 if (typeof(this.groups[groupId]) == 'undefined') {
23697 return this.groups[groupId] ;
23710 * @class Roo.bootstrap.Radio
23711 * @extends Roo.bootstrap.Component
23712 * Bootstrap Radio class
23713 * @cfg {String} boxLabel - the label associated
23714 * @cfg {String} value - the value of radio
23717 * Create a new Radio
23718 * @param {Object} config The config object
23720 Roo.bootstrap.Radio = function(config){
23721 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23725 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23731 getAutoCreate : function()
23735 cls : 'form-group radio',
23740 html : this.boxLabel
23748 initEvents : function()
23750 this.parent().register(this);
23752 this.el.on('click', this.onClick, this);
23756 onClick : function(e)
23758 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23759 this.setChecked(true);
23763 setChecked : function(state, suppressEvent)
23765 this.parent().setValue(this.value, suppressEvent);
23769 setBoxLabel : function(v)
23774 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23789 * @class Roo.bootstrap.SecurePass
23790 * @extends Roo.bootstrap.Input
23791 * Bootstrap SecurePass class
23795 * Create a new SecurePass
23796 * @param {Object} config The config object
23799 Roo.bootstrap.SecurePass = function (config) {
23800 // these go here, so the translation tool can replace them..
23802 PwdEmpty: "Please type a password, and then retype it to confirm.",
23803 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23804 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23805 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23806 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23807 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23808 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23809 TooWeak: "Your password is Too Weak."
23811 this.meterLabel = "Password strength:";
23812 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23813 this.meterClass = [
23814 "roo-password-meter-tooweak",
23815 "roo-password-meter-weak",
23816 "roo-password-meter-medium",
23817 "roo-password-meter-strong",
23818 "roo-password-meter-grey"
23823 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23826 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23828 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23830 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23831 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23832 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23833 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23834 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23835 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23836 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23846 * @cfg {String/Object} Label for the strength meter (defaults to
23847 * 'Password strength:')
23852 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23853 * ['Weak', 'Medium', 'Strong'])
23856 pwdStrengths: false,
23869 initEvents: function ()
23871 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23873 if (this.el.is('input[type=password]') && Roo.isSafari) {
23874 this.el.on('keydown', this.SafariOnKeyDown, this);
23877 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23880 onRender: function (ct, position)
23882 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23883 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23884 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23886 this.trigger.createChild({
23891 cls: 'roo-password-meter-grey col-xs-12',
23894 //width: this.meterWidth + 'px'
23898 cls: 'roo-password-meter-text'
23904 if (this.hideTrigger) {
23905 this.trigger.setDisplayed(false);
23907 this.setSize(this.width || '', this.height || '');
23910 onDestroy: function ()
23912 if (this.trigger) {
23913 this.trigger.removeAllListeners();
23914 this.trigger.remove();
23917 this.wrap.remove();
23919 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23922 checkStrength: function ()
23924 var pwd = this.inputEl().getValue();
23925 if (pwd == this._lastPwd) {
23930 if (this.ClientSideStrongPassword(pwd)) {
23932 } else if (this.ClientSideMediumPassword(pwd)) {
23934 } else if (this.ClientSideWeakPassword(pwd)) {
23940 Roo.log('strength1: ' + strength);
23942 //var pm = this.trigger.child('div/div/div').dom;
23943 var pm = this.trigger.child('div/div');
23944 pm.removeClass(this.meterClass);
23945 pm.addClass(this.meterClass[strength]);
23948 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23950 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23952 this._lastPwd = pwd;
23956 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23958 this._lastPwd = '';
23960 var pm = this.trigger.child('div/div');
23961 pm.removeClass(this.meterClass);
23962 pm.addClass('roo-password-meter-grey');
23965 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23968 this.inputEl().dom.type='password';
23971 validateValue: function (value)
23973 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23976 if (value.length == 0) {
23977 if (this.allowBlank) {
23978 this.clearInvalid();
23982 this.markInvalid(this.errors.PwdEmpty);
23983 this.errorMsg = this.errors.PwdEmpty;
23991 if (!value.match(/[\x21-\x7e]+/)) {
23992 this.markInvalid(this.errors.PwdBadChar);
23993 this.errorMsg = this.errors.PwdBadChar;
23996 if (value.length < 6) {
23997 this.markInvalid(this.errors.PwdShort);
23998 this.errorMsg = this.errors.PwdShort;
24001 if (value.length > 16) {
24002 this.markInvalid(this.errors.PwdLong);
24003 this.errorMsg = this.errors.PwdLong;
24007 if (this.ClientSideStrongPassword(value)) {
24009 } else if (this.ClientSideMediumPassword(value)) {
24011 } else if (this.ClientSideWeakPassword(value)) {
24018 if (strength < 2) {
24019 //this.markInvalid(this.errors.TooWeak);
24020 this.errorMsg = this.errors.TooWeak;
24025 console.log('strength2: ' + strength);
24027 //var pm = this.trigger.child('div/div/div').dom;
24029 var pm = this.trigger.child('div/div');
24030 pm.removeClass(this.meterClass);
24031 pm.addClass(this.meterClass[strength]);
24033 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24035 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24037 this.errorMsg = '';
24041 CharacterSetChecks: function (type)
24044 this.fResult = false;
24047 isctype: function (character, type)
24050 case this.kCapitalLetter:
24051 if (character >= 'A' && character <= 'Z') {
24056 case this.kSmallLetter:
24057 if (character >= 'a' && character <= 'z') {
24063 if (character >= '0' && character <= '9') {
24068 case this.kPunctuation:
24069 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24080 IsLongEnough: function (pwd, size)
24082 return !(pwd == null || isNaN(size) || pwd.length < size);
24085 SpansEnoughCharacterSets: function (word, nb)
24087 if (!this.IsLongEnough(word, nb))
24092 var characterSetChecks = new Array(
24093 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24094 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24097 for (var index = 0; index < word.length; ++index) {
24098 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24099 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24100 characterSetChecks[nCharSet].fResult = true;
24107 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24108 if (characterSetChecks[nCharSet].fResult) {
24113 if (nCharSets < nb) {
24119 ClientSideStrongPassword: function (pwd)
24121 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24124 ClientSideMediumPassword: function (pwd)
24126 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24129 ClientSideWeakPassword: function (pwd)
24131 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24134 })//<script type="text/javascript">
24137 * Based Ext JS Library 1.1.1
24138 * Copyright(c) 2006-2007, Ext JS, LLC.
24144 * @class Roo.HtmlEditorCore
24145 * @extends Roo.Component
24146 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24148 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24151 Roo.HtmlEditorCore = function(config){
24154 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24159 * @event initialize
24160 * Fires when the editor is fully initialized (including the iframe)
24161 * @param {Roo.HtmlEditorCore} this
24166 * Fires when the editor is first receives the focus. Any insertion must wait
24167 * until after this event.
24168 * @param {Roo.HtmlEditorCore} this
24172 * @event beforesync
24173 * Fires before the textarea is updated with content from the editor iframe. Return false
24174 * to cancel the sync.
24175 * @param {Roo.HtmlEditorCore} this
24176 * @param {String} html
24180 * @event beforepush
24181 * Fires before the iframe editor is updated with content from the textarea. Return false
24182 * to cancel the push.
24183 * @param {Roo.HtmlEditorCore} this
24184 * @param {String} html
24189 * Fires when the textarea is updated with content from the editor iframe.
24190 * @param {Roo.HtmlEditorCore} this
24191 * @param {String} html
24196 * Fires when the iframe editor is updated with content from the textarea.
24197 * @param {Roo.HtmlEditorCore} this
24198 * @param {String} html
24203 * @event editorevent
24204 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24205 * @param {Roo.HtmlEditorCore} this
24211 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24213 // defaults : white / black...
24214 this.applyBlacklists();
24221 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24225 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24231 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24236 * @cfg {Number} height (in pixels)
24240 * @cfg {Number} width (in pixels)
24245 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24248 stylesheets: false,
24253 // private properties
24254 validationEvent : false,
24256 initialized : false,
24258 sourceEditMode : false,
24259 onFocus : Roo.emptyFn,
24261 hideMode:'offsets',
24265 // blacklist + whitelisted elements..
24272 * Protected method that will not generally be called directly. It
24273 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24274 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24276 getDocMarkup : function(){
24280 // inherit styels from page...??
24281 if (this.stylesheets === false) {
24283 Roo.get(document.head).select('style').each(function(node) {
24284 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24287 Roo.get(document.head).select('link').each(function(node) {
24288 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24291 } else if (!this.stylesheets.length) {
24293 st = '<style type="text/css">' +
24294 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24297 for (var i in this.stylesheets) {
24298 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24303 st += '<style type="text/css">' +
24304 'IMG { cursor: pointer } ' +
24307 var cls = 'roo-htmleditor-body';
24309 if(this.bodyCls.length){
24310 cls += ' ' + this.bodyCls;
24313 return '<html><head>' + st +
24314 //<style type="text/css">' +
24315 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24317 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24321 onRender : function(ct, position)
24324 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24325 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24328 this.el.dom.style.border = '0 none';
24329 this.el.dom.setAttribute('tabIndex', -1);
24330 this.el.addClass('x-hidden hide');
24334 if(Roo.isIE){ // fix IE 1px bogus margin
24335 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24339 this.frameId = Roo.id();
24343 var iframe = this.owner.wrap.createChild({
24345 cls: 'form-control', // bootstrap..
24347 name: this.frameId,
24348 frameBorder : 'no',
24349 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24354 this.iframe = iframe.dom;
24356 this.assignDocWin();
24358 this.doc.designMode = 'on';
24361 this.doc.write(this.getDocMarkup());
24365 var task = { // must defer to wait for browser to be ready
24367 //console.log("run task?" + this.doc.readyState);
24368 this.assignDocWin();
24369 if(this.doc.body || this.doc.readyState == 'complete'){
24371 this.doc.designMode="on";
24375 Roo.TaskMgr.stop(task);
24376 this.initEditor.defer(10, this);
24383 Roo.TaskMgr.start(task);
24388 onResize : function(w, h)
24390 Roo.log('resize: ' +w + ',' + h );
24391 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24395 if(typeof w == 'number'){
24397 this.iframe.style.width = w + 'px';
24399 if(typeof h == 'number'){
24401 this.iframe.style.height = h + 'px';
24403 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24410 * Toggles the editor between standard and source edit mode.
24411 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24413 toggleSourceEdit : function(sourceEditMode){
24415 this.sourceEditMode = sourceEditMode === true;
24417 if(this.sourceEditMode){
24419 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24422 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24423 //this.iframe.className = '';
24426 //this.setSize(this.owner.wrap.getSize());
24427 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24434 * Protected method that will not generally be called directly. If you need/want
24435 * custom HTML cleanup, this is the method you should override.
24436 * @param {String} html The HTML to be cleaned
24437 * return {String} The cleaned HTML
24439 cleanHtml : function(html){
24440 html = String(html);
24441 if(html.length > 5){
24442 if(Roo.isSafari){ // strip safari nonsense
24443 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24446 if(html == ' '){
24453 * HTML Editor -> Textarea
24454 * Protected method that will not generally be called directly. Syncs the contents
24455 * of the editor iframe with the textarea.
24457 syncValue : function(){
24458 if(this.initialized){
24459 var bd = (this.doc.body || this.doc.documentElement);
24460 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24461 var html = bd.innerHTML;
24463 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24464 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24466 html = '<div style="'+m[0]+'">' + html + '</div>';
24469 html = this.cleanHtml(html);
24470 // fix up the special chars.. normaly like back quotes in word...
24471 // however we do not want to do this with chinese..
24472 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24474 var cc = match.charCodeAt();
24476 // Get the character value, handling surrogate pairs
24477 if (match.length == 2) {
24478 // It's a surrogate pair, calculate the Unicode code point
24479 var high = match.charCodeAt(0) - 0xD800;
24480 var low = match.charCodeAt(1) - 0xDC00;
24481 cc = (high * 0x400) + low + 0x10000;
24483 (cc >= 0x4E00 && cc < 0xA000 ) ||
24484 (cc >= 0x3400 && cc < 0x4E00 ) ||
24485 (cc >= 0xf900 && cc < 0xfb00 )
24490 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24491 return "&#" + cc + ";";
24498 if(this.owner.fireEvent('beforesync', this, html) !== false){
24499 this.el.dom.value = html;
24500 this.owner.fireEvent('sync', this, html);
24506 * Protected method that will not generally be called directly. Pushes the value of the textarea
24507 * into the iframe editor.
24509 pushValue : function(){
24510 if(this.initialized){
24511 var v = this.el.dom.value.trim();
24513 // if(v.length < 1){
24517 if(this.owner.fireEvent('beforepush', this, v) !== false){
24518 var d = (this.doc.body || this.doc.documentElement);
24520 this.cleanUpPaste();
24521 this.el.dom.value = d.innerHTML;
24522 this.owner.fireEvent('push', this, v);
24528 deferFocus : function(){
24529 this.focus.defer(10, this);
24533 focus : function(){
24534 if(this.win && !this.sourceEditMode){
24541 assignDocWin: function()
24543 var iframe = this.iframe;
24546 this.doc = iframe.contentWindow.document;
24547 this.win = iframe.contentWindow;
24549 // if (!Roo.get(this.frameId)) {
24552 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24553 // this.win = Roo.get(this.frameId).dom.contentWindow;
24555 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24559 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24560 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24565 initEditor : function(){
24566 //console.log("INIT EDITOR");
24567 this.assignDocWin();
24571 this.doc.designMode="on";
24573 this.doc.write(this.getDocMarkup());
24576 var dbody = (this.doc.body || this.doc.documentElement);
24577 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24578 // this copies styles from the containing element into thsi one..
24579 // not sure why we need all of this..
24580 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24582 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24583 //ss['background-attachment'] = 'fixed'; // w3c
24584 dbody.bgProperties = 'fixed'; // ie
24585 //Roo.DomHelper.applyStyles(dbody, ss);
24586 Roo.EventManager.on(this.doc, {
24587 //'mousedown': this.onEditorEvent,
24588 'mouseup': this.onEditorEvent,
24589 'dblclick': this.onEditorEvent,
24590 'click': this.onEditorEvent,
24591 'keyup': this.onEditorEvent,
24596 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24598 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24599 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24601 this.initialized = true;
24603 this.owner.fireEvent('initialize', this);
24608 onDestroy : function(){
24614 //for (var i =0; i < this.toolbars.length;i++) {
24615 // // fixme - ask toolbars for heights?
24616 // this.toolbars[i].onDestroy();
24619 //this.wrap.dom.innerHTML = '';
24620 //this.wrap.remove();
24625 onFirstFocus : function(){
24627 this.assignDocWin();
24630 this.activated = true;
24633 if(Roo.isGecko){ // prevent silly gecko errors
24635 var s = this.win.getSelection();
24636 if(!s.focusNode || s.focusNode.nodeType != 3){
24637 var r = s.getRangeAt(0);
24638 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24643 this.execCmd('useCSS', true);
24644 this.execCmd('styleWithCSS', false);
24647 this.owner.fireEvent('activate', this);
24651 adjustFont: function(btn){
24652 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24653 //if(Roo.isSafari){ // safari
24656 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24657 if(Roo.isSafari){ // safari
24658 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24659 v = (v < 10) ? 10 : v;
24660 v = (v > 48) ? 48 : v;
24661 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24666 v = Math.max(1, v+adjust);
24668 this.execCmd('FontSize', v );
24671 onEditorEvent : function(e)
24673 this.owner.fireEvent('editorevent', this, e);
24674 // this.updateToolbar();
24675 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24678 insertTag : function(tg)
24680 // could be a bit smarter... -> wrap the current selected tRoo..
24681 if (tg.toLowerCase() == 'span' ||
24682 tg.toLowerCase() == 'code' ||
24683 tg.toLowerCase() == 'sup' ||
24684 tg.toLowerCase() == 'sub'
24687 range = this.createRange(this.getSelection());
24688 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24689 wrappingNode.appendChild(range.extractContents());
24690 range.insertNode(wrappingNode);
24697 this.execCmd("formatblock", tg);
24701 insertText : function(txt)
24705 var range = this.createRange();
24706 range.deleteContents();
24707 //alert(Sender.getAttribute('label'));
24709 range.insertNode(this.doc.createTextNode(txt));
24715 * Executes a Midas editor command on the editor document and performs necessary focus and
24716 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24717 * @param {String} cmd The Midas command
24718 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24720 relayCmd : function(cmd, value){
24722 this.execCmd(cmd, value);
24723 this.owner.fireEvent('editorevent', this);
24724 //this.updateToolbar();
24725 this.owner.deferFocus();
24729 * Executes a Midas editor command directly on the editor document.
24730 * For visual commands, you should use {@link #relayCmd} instead.
24731 * <b>This should only be called after the editor is initialized.</b>
24732 * @param {String} cmd The Midas command
24733 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24735 execCmd : function(cmd, value){
24736 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24743 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24745 * @param {String} text | dom node..
24747 insertAtCursor : function(text)
24750 if(!this.activated){
24756 var r = this.doc.selection.createRange();
24767 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24771 // from jquery ui (MIT licenced)
24773 var win = this.win;
24775 if (win.getSelection && win.getSelection().getRangeAt) {
24776 range = win.getSelection().getRangeAt(0);
24777 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24778 range.insertNode(node);
24779 } else if (win.document.selection && win.document.selection.createRange) {
24780 // no firefox support
24781 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24782 win.document.selection.createRange().pasteHTML(txt);
24784 // no firefox support
24785 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24786 this.execCmd('InsertHTML', txt);
24795 mozKeyPress : function(e){
24797 var c = e.getCharCode(), cmd;
24800 c = String.fromCharCode(c).toLowerCase();
24814 this.cleanUpPaste.defer(100, this);
24822 e.preventDefault();
24830 fixKeys : function(){ // load time branching for fastest keydown performance
24832 return function(e){
24833 var k = e.getKey(), r;
24836 r = this.doc.selection.createRange();
24839 r.pasteHTML('    ');
24846 r = this.doc.selection.createRange();
24848 var target = r.parentElement();
24849 if(!target || target.tagName.toLowerCase() != 'li'){
24851 r.pasteHTML('<br />');
24857 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24858 this.cleanUpPaste.defer(100, this);
24864 }else if(Roo.isOpera){
24865 return function(e){
24866 var k = e.getKey();
24870 this.execCmd('InsertHTML','    ');
24873 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24874 this.cleanUpPaste.defer(100, this);
24879 }else if(Roo.isSafari){
24880 return function(e){
24881 var k = e.getKey();
24885 this.execCmd('InsertText','\t');
24889 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24890 this.cleanUpPaste.defer(100, this);
24898 getAllAncestors: function()
24900 var p = this.getSelectedNode();
24903 a.push(p); // push blank onto stack..
24904 p = this.getParentElement();
24908 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24912 a.push(this.doc.body);
24916 lastSelNode : false,
24919 getSelection : function()
24921 this.assignDocWin();
24922 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24925 getSelectedNode: function()
24927 // this may only work on Gecko!!!
24929 // should we cache this!!!!
24934 var range = this.createRange(this.getSelection()).cloneRange();
24937 var parent = range.parentElement();
24939 var testRange = range.duplicate();
24940 testRange.moveToElementText(parent);
24941 if (testRange.inRange(range)) {
24944 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24947 parent = parent.parentElement;
24952 // is ancestor a text element.
24953 var ac = range.commonAncestorContainer;
24954 if (ac.nodeType == 3) {
24955 ac = ac.parentNode;
24958 var ar = ac.childNodes;
24961 var other_nodes = [];
24962 var has_other_nodes = false;
24963 for (var i=0;i<ar.length;i++) {
24964 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24967 // fullly contained node.
24969 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24974 // probably selected..
24975 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24976 other_nodes.push(ar[i]);
24980 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24985 has_other_nodes = true;
24987 if (!nodes.length && other_nodes.length) {
24988 nodes= other_nodes;
24990 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24996 createRange: function(sel)
24998 // this has strange effects when using with
24999 // top toolbar - not sure if it's a great idea.
25000 //this.editor.contentWindow.focus();
25001 if (typeof sel != "undefined") {
25003 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25005 return this.doc.createRange();
25008 return this.doc.createRange();
25011 getParentElement: function()
25014 this.assignDocWin();
25015 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25017 var range = this.createRange(sel);
25020 var p = range.commonAncestorContainer;
25021 while (p.nodeType == 3) { // text node
25032 * Range intersection.. the hard stuff...
25036 * [ -- selected range --- ]
25040 * if end is before start or hits it. fail.
25041 * if start is after end or hits it fail.
25043 * if either hits (but other is outside. - then it's not
25049 // @see http://www.thismuchiknow.co.uk/?p=64.
25050 rangeIntersectsNode : function(range, node)
25052 var nodeRange = node.ownerDocument.createRange();
25054 nodeRange.selectNode(node);
25056 nodeRange.selectNodeContents(node);
25059 var rangeStartRange = range.cloneRange();
25060 rangeStartRange.collapse(true);
25062 var rangeEndRange = range.cloneRange();
25063 rangeEndRange.collapse(false);
25065 var nodeStartRange = nodeRange.cloneRange();
25066 nodeStartRange.collapse(true);
25068 var nodeEndRange = nodeRange.cloneRange();
25069 nodeEndRange.collapse(false);
25071 return rangeStartRange.compareBoundaryPoints(
25072 Range.START_TO_START, nodeEndRange) == -1 &&
25073 rangeEndRange.compareBoundaryPoints(
25074 Range.START_TO_START, nodeStartRange) == 1;
25078 rangeCompareNode : function(range, node)
25080 var nodeRange = node.ownerDocument.createRange();
25082 nodeRange.selectNode(node);
25084 nodeRange.selectNodeContents(node);
25088 range.collapse(true);
25090 nodeRange.collapse(true);
25092 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25093 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25095 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25097 var nodeIsBefore = ss == 1;
25098 var nodeIsAfter = ee == -1;
25100 if (nodeIsBefore && nodeIsAfter) {
25103 if (!nodeIsBefore && nodeIsAfter) {
25104 return 1; //right trailed.
25107 if (nodeIsBefore && !nodeIsAfter) {
25108 return 2; // left trailed.
25114 // private? - in a new class?
25115 cleanUpPaste : function()
25117 // cleans up the whole document..
25118 Roo.log('cleanuppaste');
25120 this.cleanUpChildren(this.doc.body);
25121 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25122 if (clean != this.doc.body.innerHTML) {
25123 this.doc.body.innerHTML = clean;
25128 cleanWordChars : function(input) {// change the chars to hex code
25129 var he = Roo.HtmlEditorCore;
25131 var output = input;
25132 Roo.each(he.swapCodes, function(sw) {
25133 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25135 output = output.replace(swapper, sw[1]);
25142 cleanUpChildren : function (n)
25144 if (!n.childNodes.length) {
25147 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25148 this.cleanUpChild(n.childNodes[i]);
25155 cleanUpChild : function (node)
25158 //console.log(node);
25159 if (node.nodeName == "#text") {
25160 // clean up silly Windows -- stuff?
25163 if (node.nodeName == "#comment") {
25164 node.parentNode.removeChild(node);
25165 // clean up silly Windows -- stuff?
25168 var lcname = node.tagName.toLowerCase();
25169 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25170 // whitelist of tags..
25172 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25174 node.parentNode.removeChild(node);
25179 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25181 // spans with no attributes - just remove them..
25182 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25183 remove_keep_children = true;
25186 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25187 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25189 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25190 // remove_keep_children = true;
25193 if (remove_keep_children) {
25194 this.cleanUpChildren(node);
25195 // inserts everything just before this node...
25196 while (node.childNodes.length) {
25197 var cn = node.childNodes[0];
25198 node.removeChild(cn);
25199 node.parentNode.insertBefore(cn, node);
25201 node.parentNode.removeChild(node);
25205 if (!node.attributes || !node.attributes.length) {
25210 this.cleanUpChildren(node);
25214 function cleanAttr(n,v)
25217 if (v.match(/^\./) || v.match(/^\//)) {
25220 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25223 if (v.match(/^#/)) {
25226 if (v.match(/^\{/)) { // allow template editing.
25229 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25230 node.removeAttribute(n);
25234 var cwhite = this.cwhite;
25235 var cblack = this.cblack;
25237 function cleanStyle(n,v)
25239 if (v.match(/expression/)) { //XSS?? should we even bother..
25240 node.removeAttribute(n);
25244 var parts = v.split(/;/);
25247 Roo.each(parts, function(p) {
25248 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25252 var l = p.split(':').shift().replace(/\s+/g,'');
25253 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25255 if ( cwhite.length && cblack.indexOf(l) > -1) {
25256 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25257 //node.removeAttribute(n);
25261 // only allow 'c whitelisted system attributes'
25262 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25263 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25264 //node.removeAttribute(n);
25274 if (clean.length) {
25275 node.setAttribute(n, clean.join(';'));
25277 node.removeAttribute(n);
25283 for (var i = node.attributes.length-1; i > -1 ; i--) {
25284 var a = node.attributes[i];
25287 if (a.name.toLowerCase().substr(0,2)=='on') {
25288 node.removeAttribute(a.name);
25291 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25292 node.removeAttribute(a.name);
25295 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25296 cleanAttr(a.name,a.value); // fixme..
25299 if (a.name == 'style') {
25300 cleanStyle(a.name,a.value);
25303 /// clean up MS crap..
25304 // tecnically this should be a list of valid class'es..
25307 if (a.name == 'class') {
25308 if (a.value.match(/^Mso/)) {
25309 node.removeAttribute('class');
25312 if (a.value.match(/^body$/)) {
25313 node.removeAttribute('class');
25324 this.cleanUpChildren(node);
25330 * Clean up MS wordisms...
25332 cleanWord : function(node)
25335 this.cleanWord(this.doc.body);
25340 node.nodeName == 'SPAN' &&
25341 !node.hasAttributes() &&
25342 node.childNodes.length == 1 &&
25343 node.firstChild.nodeName == "#text"
25345 var textNode = node.firstChild;
25346 node.removeChild(textNode);
25347 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25348 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25350 node.parentNode.insertBefore(textNode, node);
25351 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25352 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25354 node.parentNode.removeChild(node);
25357 if (node.nodeName == "#text") {
25358 // clean up silly Windows -- stuff?
25361 if (node.nodeName == "#comment") {
25362 node.parentNode.removeChild(node);
25363 // clean up silly Windows -- stuff?
25367 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25368 node.parentNode.removeChild(node);
25371 //Roo.log(node.tagName);
25372 // remove - but keep children..
25373 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25374 //Roo.log('-- removed');
25375 while (node.childNodes.length) {
25376 var cn = node.childNodes[0];
25377 node.removeChild(cn);
25378 node.parentNode.insertBefore(cn, node);
25379 // move node to parent - and clean it..
25380 this.cleanWord(cn);
25382 node.parentNode.removeChild(node);
25383 /// no need to iterate chidlren = it's got none..
25384 //this.iterateChildren(node, this.cleanWord);
25388 if (node.className.length) {
25390 var cn = node.className.split(/\W+/);
25392 Roo.each(cn, function(cls) {
25393 if (cls.match(/Mso[a-zA-Z]+/)) {
25398 node.className = cna.length ? cna.join(' ') : '';
25400 node.removeAttribute("class");
25404 if (node.hasAttribute("lang")) {
25405 node.removeAttribute("lang");
25408 if (node.hasAttribute("style")) {
25410 var styles = node.getAttribute("style").split(";");
25412 Roo.each(styles, function(s) {
25413 if (!s.match(/:/)) {
25416 var kv = s.split(":");
25417 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25420 // what ever is left... we allow.
25423 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25424 if (!nstyle.length) {
25425 node.removeAttribute('style');
25428 this.iterateChildren(node, this.cleanWord);
25434 * iterateChildren of a Node, calling fn each time, using this as the scole..
25435 * @param {DomNode} node node to iterate children of.
25436 * @param {Function} fn method of this class to call on each item.
25438 iterateChildren : function(node, fn)
25440 if (!node.childNodes.length) {
25443 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25444 fn.call(this, node.childNodes[i])
25450 * cleanTableWidths.
25452 * Quite often pasting from word etc.. results in tables with column and widths.
25453 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25456 cleanTableWidths : function(node)
25461 this.cleanTableWidths(this.doc.body);
25466 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25469 Roo.log(node.tagName);
25470 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25471 this.iterateChildren(node, this.cleanTableWidths);
25474 if (node.hasAttribute('width')) {
25475 node.removeAttribute('width');
25479 if (node.hasAttribute("style")) {
25482 var styles = node.getAttribute("style").split(";");
25484 Roo.each(styles, function(s) {
25485 if (!s.match(/:/)) {
25488 var kv = s.split(":");
25489 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25492 // what ever is left... we allow.
25495 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25496 if (!nstyle.length) {
25497 node.removeAttribute('style');
25501 this.iterateChildren(node, this.cleanTableWidths);
25509 domToHTML : function(currentElement, depth, nopadtext) {
25511 depth = depth || 0;
25512 nopadtext = nopadtext || false;
25514 if (!currentElement) {
25515 return this.domToHTML(this.doc.body);
25518 //Roo.log(currentElement);
25520 var allText = false;
25521 var nodeName = currentElement.nodeName;
25522 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25524 if (nodeName == '#text') {
25526 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25531 if (nodeName != 'BODY') {
25534 // Prints the node tagName, such as <A>, <IMG>, etc
25537 for(i = 0; i < currentElement.attributes.length;i++) {
25539 var aname = currentElement.attributes.item(i).name;
25540 if (!currentElement.attributes.item(i).value.length) {
25543 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25546 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25555 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25558 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25563 // Traverse the tree
25565 var currentElementChild = currentElement.childNodes.item(i);
25566 var allText = true;
25567 var innerHTML = '';
25569 while (currentElementChild) {
25570 // Formatting code (indent the tree so it looks nice on the screen)
25571 var nopad = nopadtext;
25572 if (lastnode == 'SPAN') {
25576 if (currentElementChild.nodeName == '#text') {
25577 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25578 toadd = nopadtext ? toadd : toadd.trim();
25579 if (!nopad && toadd.length > 80) {
25580 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25582 innerHTML += toadd;
25585 currentElementChild = currentElement.childNodes.item(i);
25591 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25593 // Recursively traverse the tree structure of the child node
25594 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25595 lastnode = currentElementChild.nodeName;
25597 currentElementChild=currentElement.childNodes.item(i);
25603 // The remaining code is mostly for formatting the tree
25604 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25609 ret+= "</"+tagName+">";
25615 applyBlacklists : function()
25617 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25618 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25622 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25623 if (b.indexOf(tag) > -1) {
25626 this.white.push(tag);
25630 Roo.each(w, function(tag) {
25631 if (b.indexOf(tag) > -1) {
25634 if (this.white.indexOf(tag) > -1) {
25637 this.white.push(tag);
25642 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25643 if (w.indexOf(tag) > -1) {
25646 this.black.push(tag);
25650 Roo.each(b, function(tag) {
25651 if (w.indexOf(tag) > -1) {
25654 if (this.black.indexOf(tag) > -1) {
25657 this.black.push(tag);
25662 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25663 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25667 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25668 if (b.indexOf(tag) > -1) {
25671 this.cwhite.push(tag);
25675 Roo.each(w, function(tag) {
25676 if (b.indexOf(tag) > -1) {
25679 if (this.cwhite.indexOf(tag) > -1) {
25682 this.cwhite.push(tag);
25687 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25688 if (w.indexOf(tag) > -1) {
25691 this.cblack.push(tag);
25695 Roo.each(b, function(tag) {
25696 if (w.indexOf(tag) > -1) {
25699 if (this.cblack.indexOf(tag) > -1) {
25702 this.cblack.push(tag);
25707 setStylesheets : function(stylesheets)
25709 if(typeof(stylesheets) == 'string'){
25710 Roo.get(this.iframe.contentDocument.head).createChild({
25712 rel : 'stylesheet',
25721 Roo.each(stylesheets, function(s) {
25726 Roo.get(_this.iframe.contentDocument.head).createChild({
25728 rel : 'stylesheet',
25737 removeStylesheets : function()
25741 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25746 setStyle : function(style)
25748 Roo.get(this.iframe.contentDocument.head).createChild({
25757 // hide stuff that is not compatible
25771 * @event specialkey
25775 * @cfg {String} fieldClass @hide
25778 * @cfg {String} focusClass @hide
25781 * @cfg {String} autoCreate @hide
25784 * @cfg {String} inputType @hide
25787 * @cfg {String} invalidClass @hide
25790 * @cfg {String} invalidText @hide
25793 * @cfg {String} msgFx @hide
25796 * @cfg {String} validateOnBlur @hide
25800 Roo.HtmlEditorCore.white = [
25801 'area', 'br', 'img', 'input', 'hr', 'wbr',
25803 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25804 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25805 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25806 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25807 'table', 'ul', 'xmp',
25809 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25812 'dir', 'menu', 'ol', 'ul', 'dl',
25818 Roo.HtmlEditorCore.black = [
25819 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25821 'base', 'basefont', 'bgsound', 'blink', 'body',
25822 'frame', 'frameset', 'head', 'html', 'ilayer',
25823 'iframe', 'layer', 'link', 'meta', 'object',
25824 'script', 'style' ,'title', 'xml' // clean later..
25826 Roo.HtmlEditorCore.clean = [
25827 'script', 'style', 'title', 'xml'
25829 Roo.HtmlEditorCore.remove = [
25834 Roo.HtmlEditorCore.ablack = [
25838 Roo.HtmlEditorCore.aclean = [
25839 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25843 Roo.HtmlEditorCore.pwhite= [
25844 'http', 'https', 'mailto'
25847 // white listed style attributes.
25848 Roo.HtmlEditorCore.cwhite= [
25849 // 'text-align', /// default is to allow most things..
25855 // black listed style attributes.
25856 Roo.HtmlEditorCore.cblack= [
25857 // 'font-size' -- this can be set by the project
25861 Roo.HtmlEditorCore.swapCodes =[
25862 [ 8211, "–" ],
25863 [ 8212, "—" ],
25880 * @class Roo.bootstrap.HtmlEditor
25881 * @extends Roo.bootstrap.TextArea
25882 * Bootstrap HtmlEditor class
25885 * Create a new HtmlEditor
25886 * @param {Object} config The config object
25889 Roo.bootstrap.HtmlEditor = function(config){
25890 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25891 if (!this.toolbars) {
25892 this.toolbars = [];
25895 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25898 * @event initialize
25899 * Fires when the editor is fully initialized (including the iframe)
25900 * @param {HtmlEditor} this
25905 * Fires when the editor is first receives the focus. Any insertion must wait
25906 * until after this event.
25907 * @param {HtmlEditor} this
25911 * @event beforesync
25912 * Fires before the textarea is updated with content from the editor iframe. Return false
25913 * to cancel the sync.
25914 * @param {HtmlEditor} this
25915 * @param {String} html
25919 * @event beforepush
25920 * Fires before the iframe editor is updated with content from the textarea. Return false
25921 * to cancel the push.
25922 * @param {HtmlEditor} this
25923 * @param {String} html
25928 * Fires when the textarea is updated with content from the editor iframe.
25929 * @param {HtmlEditor} this
25930 * @param {String} html
25935 * Fires when the iframe editor is updated with content from the textarea.
25936 * @param {HtmlEditor} this
25937 * @param {String} html
25941 * @event editmodechange
25942 * Fires when the editor switches edit modes
25943 * @param {HtmlEditor} this
25944 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25946 editmodechange: true,
25948 * @event editorevent
25949 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25950 * @param {HtmlEditor} this
25954 * @event firstfocus
25955 * Fires when on first focus - needed by toolbars..
25956 * @param {HtmlEditor} this
25961 * Auto save the htmlEditor value as a file into Events
25962 * @param {HtmlEditor} this
25966 * @event savedpreview
25967 * preview the saved version of htmlEditor
25968 * @param {HtmlEditor} this
25975 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25979 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25984 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25989 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25994 * @cfg {Number} height (in pixels)
25998 * @cfg {Number} width (in pixels)
26003 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26006 stylesheets: false,
26011 // private properties
26012 validationEvent : false,
26014 initialized : false,
26017 onFocus : Roo.emptyFn,
26019 hideMode:'offsets',
26021 tbContainer : false,
26025 toolbarContainer :function() {
26026 return this.wrap.select('.x-html-editor-tb',true).first();
26030 * Protected method that will not generally be called directly. It
26031 * is called when the editor creates its toolbar. Override this method if you need to
26032 * add custom toolbar buttons.
26033 * @param {HtmlEditor} editor
26035 createToolbar : function(){
26036 Roo.log('renewing');
26037 Roo.log("create toolbars");
26039 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26040 this.toolbars[0].render(this.toolbarContainer());
26044 // if (!editor.toolbars || !editor.toolbars.length) {
26045 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26048 // for (var i =0 ; i < editor.toolbars.length;i++) {
26049 // editor.toolbars[i] = Roo.factory(
26050 // typeof(editor.toolbars[i]) == 'string' ?
26051 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26052 // Roo.bootstrap.HtmlEditor);
26053 // editor.toolbars[i].init(editor);
26059 onRender : function(ct, position)
26061 // Roo.log("Call onRender: " + this.xtype);
26063 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26065 this.wrap = this.inputEl().wrap({
26066 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26069 this.editorcore.onRender(ct, position);
26071 if (this.resizable) {
26072 this.resizeEl = new Roo.Resizable(this.wrap, {
26076 minHeight : this.height,
26077 height: this.height,
26078 handles : this.resizable,
26081 resize : function(r, w, h) {
26082 _t.onResize(w,h); // -something
26088 this.createToolbar(this);
26091 if(!this.width && this.resizable){
26092 this.setSize(this.wrap.getSize());
26094 if (this.resizeEl) {
26095 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26096 // should trigger onReize..
26102 onResize : function(w, h)
26104 Roo.log('resize: ' +w + ',' + h );
26105 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26109 if(this.inputEl() ){
26110 if(typeof w == 'number'){
26111 var aw = w - this.wrap.getFrameWidth('lr');
26112 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26115 if(typeof h == 'number'){
26116 var tbh = -11; // fixme it needs to tool bar size!
26117 for (var i =0; i < this.toolbars.length;i++) {
26118 // fixme - ask toolbars for heights?
26119 tbh += this.toolbars[i].el.getHeight();
26120 //if (this.toolbars[i].footer) {
26121 // tbh += this.toolbars[i].footer.el.getHeight();
26129 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26130 ah -= 5; // knock a few pixes off for look..
26131 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26135 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26136 this.editorcore.onResize(ew,eh);
26141 * Toggles the editor between standard and source edit mode.
26142 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26144 toggleSourceEdit : function(sourceEditMode)
26146 this.editorcore.toggleSourceEdit(sourceEditMode);
26148 if(this.editorcore.sourceEditMode){
26149 Roo.log('editor - showing textarea');
26152 // Roo.log(this.syncValue());
26154 this.inputEl().removeClass(['hide', 'x-hidden']);
26155 this.inputEl().dom.removeAttribute('tabIndex');
26156 this.inputEl().focus();
26158 Roo.log('editor - hiding textarea');
26160 // Roo.log(this.pushValue());
26163 this.inputEl().addClass(['hide', 'x-hidden']);
26164 this.inputEl().dom.setAttribute('tabIndex', -1);
26165 //this.deferFocus();
26168 if(this.resizable){
26169 this.setSize(this.wrap.getSize());
26172 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26175 // private (for BoxComponent)
26176 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26178 // private (for BoxComponent)
26179 getResizeEl : function(){
26183 // private (for BoxComponent)
26184 getPositionEl : function(){
26189 initEvents : function(){
26190 this.originalValue = this.getValue();
26194 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26197 // markInvalid : Roo.emptyFn,
26199 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26202 // clearInvalid : Roo.emptyFn,
26204 setValue : function(v){
26205 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26206 this.editorcore.pushValue();
26211 deferFocus : function(){
26212 this.focus.defer(10, this);
26216 focus : function(){
26217 this.editorcore.focus();
26223 onDestroy : function(){
26229 for (var i =0; i < this.toolbars.length;i++) {
26230 // fixme - ask toolbars for heights?
26231 this.toolbars[i].onDestroy();
26234 this.wrap.dom.innerHTML = '';
26235 this.wrap.remove();
26240 onFirstFocus : function(){
26241 //Roo.log("onFirstFocus");
26242 this.editorcore.onFirstFocus();
26243 for (var i =0; i < this.toolbars.length;i++) {
26244 this.toolbars[i].onFirstFocus();
26250 syncValue : function()
26252 this.editorcore.syncValue();
26255 pushValue : function()
26257 this.editorcore.pushValue();
26261 // hide stuff that is not compatible
26275 * @event specialkey
26279 * @cfg {String} fieldClass @hide
26282 * @cfg {String} focusClass @hide
26285 * @cfg {String} autoCreate @hide
26288 * @cfg {String} inputType @hide
26292 * @cfg {String} invalidText @hide
26295 * @cfg {String} msgFx @hide
26298 * @cfg {String} validateOnBlur @hide
26307 Roo.namespace('Roo.bootstrap.htmleditor');
26309 * @class Roo.bootstrap.HtmlEditorToolbar1
26315 new Roo.bootstrap.HtmlEditor({
26318 new Roo.bootstrap.HtmlEditorToolbar1({
26319 disable : { fonts: 1 , format: 1, ..., ... , ...],
26325 * @cfg {Object} disable List of elements to disable..
26326 * @cfg {Array} btns List of additional buttons.
26330 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26333 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26336 Roo.apply(this, config);
26338 // default disabled, based on 'good practice'..
26339 this.disable = this.disable || {};
26340 Roo.applyIf(this.disable, {
26343 specialElements : true
26345 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26347 this.editor = config.editor;
26348 this.editorcore = config.editor.editorcore;
26350 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26352 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26353 // dont call parent... till later.
26355 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26360 editorcore : false,
26365 "h1","h2","h3","h4","h5","h6",
26367 "abbr", "acronym", "address", "cite", "samp", "var",
26371 onRender : function(ct, position)
26373 // Roo.log("Call onRender: " + this.xtype);
26375 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26377 this.el.dom.style.marginBottom = '0';
26379 var editorcore = this.editorcore;
26380 var editor= this.editor;
26383 var btn = function(id,cmd , toggle, handler, html){
26385 var event = toggle ? 'toggle' : 'click';
26390 xns: Roo.bootstrap,
26394 enableToggle:toggle !== false,
26396 pressed : toggle ? false : null,
26399 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26400 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26406 // var cb_box = function...
26411 xns: Roo.bootstrap,
26416 xns: Roo.bootstrap,
26420 Roo.each(this.formats, function(f) {
26421 style.menu.items.push({
26423 xns: Roo.bootstrap,
26424 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26429 editorcore.insertTag(this.tagname);
26436 children.push(style);
26438 btn('bold',false,true);
26439 btn('italic',false,true);
26440 btn('align-left', 'justifyleft',true);
26441 btn('align-center', 'justifycenter',true);
26442 btn('align-right' , 'justifyright',true);
26443 btn('link', false, false, function(btn) {
26444 //Roo.log("create link?");
26445 var url = prompt(this.createLinkText, this.defaultLinkValue);
26446 if(url && url != 'http:/'+'/'){
26447 this.editorcore.relayCmd('createlink', url);
26450 btn('list','insertunorderedlist',true);
26451 btn('pencil', false,true, function(btn){
26453 this.toggleSourceEdit(btn.pressed);
26456 if (this.editor.btns.length > 0) {
26457 for (var i = 0; i<this.editor.btns.length; i++) {
26458 children.push(this.editor.btns[i]);
26466 xns: Roo.bootstrap,
26471 xns: Roo.bootstrap,
26476 cog.menu.items.push({
26478 xns: Roo.bootstrap,
26479 html : Clean styles,
26484 editorcore.insertTag(this.tagname);
26493 this.xtype = 'NavSimplebar';
26495 for(var i=0;i< children.length;i++) {
26497 this.buttons.add(this.addxtypeChild(children[i]));
26501 editor.on('editorevent', this.updateToolbar, this);
26503 onBtnClick : function(id)
26505 this.editorcore.relayCmd(id);
26506 this.editorcore.focus();
26510 * Protected method that will not generally be called directly. It triggers
26511 * a toolbar update by reading the markup state of the current selection in the editor.
26513 updateToolbar: function(){
26515 if(!this.editorcore.activated){
26516 this.editor.onFirstFocus(); // is this neeed?
26520 var btns = this.buttons;
26521 var doc = this.editorcore.doc;
26522 btns.get('bold').setActive(doc.queryCommandState('bold'));
26523 btns.get('italic').setActive(doc.queryCommandState('italic'));
26524 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26526 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26527 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26528 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26530 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26531 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26534 var ans = this.editorcore.getAllAncestors();
26535 if (this.formatCombo) {
26538 var store = this.formatCombo.store;
26539 this.formatCombo.setValue("");
26540 for (var i =0; i < ans.length;i++) {
26541 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26543 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26551 // hides menus... - so this cant be on a menu...
26552 Roo.bootstrap.MenuMgr.hideAll();
26554 Roo.bootstrap.MenuMgr.hideAll();
26555 //this.editorsyncValue();
26557 onFirstFocus: function() {
26558 this.buttons.each(function(item){
26562 toggleSourceEdit : function(sourceEditMode){
26565 if(sourceEditMode){
26566 Roo.log("disabling buttons");
26567 this.buttons.each( function(item){
26568 if(item.cmd != 'pencil'){
26574 Roo.log("enabling buttons");
26575 if(this.editorcore.initialized){
26576 this.buttons.each( function(item){
26582 Roo.log("calling toggole on editor");
26583 // tell the editor that it's been pressed..
26584 this.editor.toggleSourceEdit(sourceEditMode);
26598 * @class Roo.bootstrap.Markdown
26599 * @extends Roo.bootstrap.TextArea
26600 * Bootstrap Showdown editable area
26601 * @cfg {string} content
26604 * Create a new Showdown
26607 Roo.bootstrap.Markdown = function(config){
26608 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26612 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26616 initEvents : function()
26619 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26620 this.markdownEl = this.el.createChild({
26621 cls : 'roo-markdown-area'
26623 this.inputEl().addClass('d-none');
26624 if (this.getValue() == '') {
26625 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26628 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26630 this.markdownEl.on('click', this.toggleTextEdit, this);
26631 this.on('blur', this.toggleTextEdit, this);
26632 this.on('specialkey', this.resizeTextArea, this);
26635 toggleTextEdit : function()
26637 var sh = this.markdownEl.getHeight();
26638 this.inputEl().addClass('d-none');
26639 this.markdownEl.addClass('d-none');
26640 if (!this.editing) {
26642 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26643 this.inputEl().removeClass('d-none');
26644 this.inputEl().focus();
26645 this.editing = true;
26648 // show showdown...
26649 this.updateMarkdown();
26650 this.markdownEl.removeClass('d-none');
26651 this.editing = false;
26654 updateMarkdown : function()
26656 if (this.getValue() == '') {
26657 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26661 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26664 resizeTextArea: function () {
26667 Roo.log([sh, this.getValue().split("\n").length * 30]);
26668 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26670 setValue : function(val)
26672 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26673 if (!this.editing) {
26674 this.updateMarkdown();
26680 if (!this.editing) {
26681 this.toggleTextEdit();
26689 * @class Roo.bootstrap.Table.AbstractSelectionModel
26690 * @extends Roo.util.Observable
26691 * Abstract base class for grid SelectionModels. It provides the interface that should be
26692 * implemented by descendant classes. This class should not be directly instantiated.
26695 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26696 this.locked = false;
26697 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26701 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26702 /** @ignore Called by the grid automatically. Do not call directly. */
26703 init : function(grid){
26709 * Locks the selections.
26712 this.locked = true;
26716 * Unlocks the selections.
26718 unlock : function(){
26719 this.locked = false;
26723 * Returns true if the selections are locked.
26724 * @return {Boolean}
26726 isLocked : function(){
26727 return this.locked;
26731 initEvents : function ()
26737 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26738 * @class Roo.bootstrap.Table.RowSelectionModel
26739 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26740 * It supports multiple selections and keyboard selection/navigation.
26742 * @param {Object} config
26745 Roo.bootstrap.Table.RowSelectionModel = function(config){
26746 Roo.apply(this, config);
26747 this.selections = new Roo.util.MixedCollection(false, function(o){
26752 this.lastActive = false;
26756 * @event selectionchange
26757 * Fires when the selection changes
26758 * @param {SelectionModel} this
26760 "selectionchange" : true,
26762 * @event afterselectionchange
26763 * Fires after the selection changes (eg. by key press or clicking)
26764 * @param {SelectionModel} this
26766 "afterselectionchange" : true,
26768 * @event beforerowselect
26769 * Fires when a row is selected being selected, return false to cancel.
26770 * @param {SelectionModel} this
26771 * @param {Number} rowIndex The selected index
26772 * @param {Boolean} keepExisting False if other selections will be cleared
26774 "beforerowselect" : true,
26777 * Fires when a row is selected.
26778 * @param {SelectionModel} this
26779 * @param {Number} rowIndex The selected index
26780 * @param {Roo.data.Record} r The record
26782 "rowselect" : true,
26784 * @event rowdeselect
26785 * Fires when a row is deselected.
26786 * @param {SelectionModel} this
26787 * @param {Number} rowIndex The selected index
26789 "rowdeselect" : true
26791 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26792 this.locked = false;
26795 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26797 * @cfg {Boolean} singleSelect
26798 * True to allow selection of only one row at a time (defaults to false)
26800 singleSelect : false,
26803 initEvents : function()
26806 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26807 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26808 //}else{ // allow click to work like normal
26809 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26811 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26812 this.grid.on("rowclick", this.handleMouseDown, this);
26814 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26815 "up" : function(e){
26817 this.selectPrevious(e.shiftKey);
26818 }else if(this.last !== false && this.lastActive !== false){
26819 var last = this.last;
26820 this.selectRange(this.last, this.lastActive-1);
26821 this.grid.getView().focusRow(this.lastActive);
26822 if(last !== false){
26826 this.selectFirstRow();
26828 this.fireEvent("afterselectionchange", this);
26830 "down" : function(e){
26832 this.selectNext(e.shiftKey);
26833 }else if(this.last !== false && this.lastActive !== false){
26834 var last = this.last;
26835 this.selectRange(this.last, this.lastActive+1);
26836 this.grid.getView().focusRow(this.lastActive);
26837 if(last !== false){
26841 this.selectFirstRow();
26843 this.fireEvent("afterselectionchange", this);
26847 this.grid.store.on('load', function(){
26848 this.selections.clear();
26851 var view = this.grid.view;
26852 view.on("refresh", this.onRefresh, this);
26853 view.on("rowupdated", this.onRowUpdated, this);
26854 view.on("rowremoved", this.onRemove, this);
26859 onRefresh : function()
26861 var ds = this.grid.store, i, v = this.grid.view;
26862 var s = this.selections;
26863 s.each(function(r){
26864 if((i = ds.indexOfId(r.id)) != -1){
26873 onRemove : function(v, index, r){
26874 this.selections.remove(r);
26878 onRowUpdated : function(v, index, r){
26879 if(this.isSelected(r)){
26880 v.onRowSelect(index);
26886 * @param {Array} records The records to select
26887 * @param {Boolean} keepExisting (optional) True to keep existing selections
26889 selectRecords : function(records, keepExisting)
26892 this.clearSelections();
26894 var ds = this.grid.store;
26895 for(var i = 0, len = records.length; i < len; i++){
26896 this.selectRow(ds.indexOf(records[i]), true);
26901 * Gets the number of selected rows.
26904 getCount : function(){
26905 return this.selections.length;
26909 * Selects the first row in the grid.
26911 selectFirstRow : function(){
26916 * Select the last row.
26917 * @param {Boolean} keepExisting (optional) True to keep existing selections
26919 selectLastRow : function(keepExisting){
26920 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26921 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26925 * Selects the row immediately following the last selected row.
26926 * @param {Boolean} keepExisting (optional) True to keep existing selections
26928 selectNext : function(keepExisting)
26930 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26931 this.selectRow(this.last+1, keepExisting);
26932 this.grid.getView().focusRow(this.last);
26937 * Selects the row that precedes the last selected row.
26938 * @param {Boolean} keepExisting (optional) True to keep existing selections
26940 selectPrevious : function(keepExisting){
26942 this.selectRow(this.last-1, keepExisting);
26943 this.grid.getView().focusRow(this.last);
26948 * Returns the selected records
26949 * @return {Array} Array of selected records
26951 getSelections : function(){
26952 return [].concat(this.selections.items);
26956 * Returns the first selected record.
26959 getSelected : function(){
26960 return this.selections.itemAt(0);
26965 * Clears all selections.
26967 clearSelections : function(fast)
26973 var ds = this.grid.store;
26974 var s = this.selections;
26975 s.each(function(r){
26976 this.deselectRow(ds.indexOfId(r.id));
26980 this.selections.clear();
26987 * Selects all rows.
26989 selectAll : function(){
26993 this.selections.clear();
26994 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26995 this.selectRow(i, true);
27000 * Returns True if there is a selection.
27001 * @return {Boolean}
27003 hasSelection : function(){
27004 return this.selections.length > 0;
27008 * Returns True if the specified row is selected.
27009 * @param {Number/Record} record The record or index of the record to check
27010 * @return {Boolean}
27012 isSelected : function(index){
27013 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27014 return (r && this.selections.key(r.id) ? true : false);
27018 * Returns True if the specified record id is selected.
27019 * @param {String} id The id of record to check
27020 * @return {Boolean}
27022 isIdSelected : function(id){
27023 return (this.selections.key(id) ? true : false);
27028 handleMouseDBClick : function(e, t){
27032 handleMouseDown : function(e, t)
27034 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27035 if(this.isLocked() || rowIndex < 0 ){
27038 if(e.shiftKey && this.last !== false){
27039 var last = this.last;
27040 this.selectRange(last, rowIndex, e.ctrlKey);
27041 this.last = last; // reset the last
27045 var isSelected = this.isSelected(rowIndex);
27046 //Roo.log("select row:" + rowIndex);
27048 this.deselectRow(rowIndex);
27050 this.selectRow(rowIndex, true);
27054 if(e.button !== 0 && isSelected){
27055 alert('rowIndex 2: ' + rowIndex);
27056 view.focusRow(rowIndex);
27057 }else if(e.ctrlKey && isSelected){
27058 this.deselectRow(rowIndex);
27059 }else if(!isSelected){
27060 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27061 view.focusRow(rowIndex);
27065 this.fireEvent("afterselectionchange", this);
27068 handleDragableRowClick : function(grid, rowIndex, e)
27070 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27071 this.selectRow(rowIndex, false);
27072 grid.view.focusRow(rowIndex);
27073 this.fireEvent("afterselectionchange", this);
27078 * Selects multiple rows.
27079 * @param {Array} rows Array of the indexes of the row to select
27080 * @param {Boolean} keepExisting (optional) True to keep existing selections
27082 selectRows : function(rows, keepExisting){
27084 this.clearSelections();
27086 for(var i = 0, len = rows.length; i < len; i++){
27087 this.selectRow(rows[i], true);
27092 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27093 * @param {Number} startRow The index of the first row in the range
27094 * @param {Number} endRow The index of the last row in the range
27095 * @param {Boolean} keepExisting (optional) True to retain existing selections
27097 selectRange : function(startRow, endRow, keepExisting){
27102 this.clearSelections();
27104 if(startRow <= endRow){
27105 for(var i = startRow; i <= endRow; i++){
27106 this.selectRow(i, true);
27109 for(var i = startRow; i >= endRow; i--){
27110 this.selectRow(i, true);
27116 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27117 * @param {Number} startRow The index of the first row in the range
27118 * @param {Number} endRow The index of the last row in the range
27120 deselectRange : function(startRow, endRow, preventViewNotify){
27124 for(var i = startRow; i <= endRow; i++){
27125 this.deselectRow(i, preventViewNotify);
27131 * @param {Number} row The index of the row to select
27132 * @param {Boolean} keepExisting (optional) True to keep existing selections
27134 selectRow : function(index, keepExisting, preventViewNotify)
27136 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27139 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27140 if(!keepExisting || this.singleSelect){
27141 this.clearSelections();
27144 var r = this.grid.store.getAt(index);
27145 //console.log('selectRow - record id :' + r.id);
27147 this.selections.add(r);
27148 this.last = this.lastActive = index;
27149 if(!preventViewNotify){
27150 var proxy = new Roo.Element(
27151 this.grid.getRowDom(index)
27153 proxy.addClass('bg-info info');
27155 this.fireEvent("rowselect", this, index, r);
27156 this.fireEvent("selectionchange", this);
27162 * @param {Number} row The index of the row to deselect
27164 deselectRow : function(index, preventViewNotify)
27169 if(this.last == index){
27172 if(this.lastActive == index){
27173 this.lastActive = false;
27176 var r = this.grid.store.getAt(index);
27181 this.selections.remove(r);
27182 //.console.log('deselectRow - record id :' + r.id);
27183 if(!preventViewNotify){
27185 var proxy = new Roo.Element(
27186 this.grid.getRowDom(index)
27188 proxy.removeClass('bg-info info');
27190 this.fireEvent("rowdeselect", this, index);
27191 this.fireEvent("selectionchange", this);
27195 restoreLast : function(){
27197 this.last = this._last;
27202 acceptsNav : function(row, col, cm){
27203 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27207 onEditorKey : function(field, e){
27208 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27213 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27215 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27217 }else if(k == e.ENTER && !e.ctrlKey){
27221 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27223 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27225 }else if(k == e.ESC){
27229 g.startEditing(newCell[0], newCell[1]);
27235 * Ext JS Library 1.1.1
27236 * Copyright(c) 2006-2007, Ext JS, LLC.
27238 * Originally Released Under LGPL - original licence link has changed is not relivant.
27241 * <script type="text/javascript">
27245 * @class Roo.bootstrap.PagingToolbar
27246 * @extends Roo.bootstrap.NavSimplebar
27247 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27249 * Create a new PagingToolbar
27250 * @param {Object} config The config object
27251 * @param {Roo.data.Store} store
27253 Roo.bootstrap.PagingToolbar = function(config)
27255 // old args format still supported... - xtype is prefered..
27256 // created from xtype...
27258 this.ds = config.dataSource;
27260 if (config.store && !this.ds) {
27261 this.store= Roo.factory(config.store, Roo.data);
27262 this.ds = this.store;
27263 this.ds.xmodule = this.xmodule || false;
27266 this.toolbarItems = [];
27267 if (config.items) {
27268 this.toolbarItems = config.items;
27271 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27276 this.bind(this.ds);
27279 if (Roo.bootstrap.version == 4) {
27280 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27282 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27287 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27289 * @cfg {Roo.data.Store} dataSource
27290 * The underlying data store providing the paged data
27293 * @cfg {String/HTMLElement/Element} container
27294 * container The id or element that will contain the toolbar
27297 * @cfg {Boolean} displayInfo
27298 * True to display the displayMsg (defaults to false)
27301 * @cfg {Number} pageSize
27302 * The number of records to display per page (defaults to 20)
27306 * @cfg {String} displayMsg
27307 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27309 displayMsg : 'Displaying {0} - {1} of {2}',
27311 * @cfg {String} emptyMsg
27312 * The message to display when no records are found (defaults to "No data to display")
27314 emptyMsg : 'No data to display',
27316 * Customizable piece of the default paging text (defaults to "Page")
27319 beforePageText : "Page",
27321 * Customizable piece of the default paging text (defaults to "of %0")
27324 afterPageText : "of {0}",
27326 * Customizable piece of the default paging text (defaults to "First Page")
27329 firstText : "First Page",
27331 * Customizable piece of the default paging text (defaults to "Previous Page")
27334 prevText : "Previous Page",
27336 * Customizable piece of the default paging text (defaults to "Next Page")
27339 nextText : "Next Page",
27341 * Customizable piece of the default paging text (defaults to "Last Page")
27344 lastText : "Last Page",
27346 * Customizable piece of the default paging text (defaults to "Refresh")
27349 refreshText : "Refresh",
27353 onRender : function(ct, position)
27355 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27356 this.navgroup.parentId = this.id;
27357 this.navgroup.onRender(this.el, null);
27358 // add the buttons to the navgroup
27360 if(this.displayInfo){
27361 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27362 this.displayEl = this.el.select('.x-paging-info', true).first();
27363 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27364 // this.displayEl = navel.el.select('span',true).first();
27370 Roo.each(_this.buttons, function(e){ // this might need to use render????
27371 Roo.factory(e).render(_this.el);
27375 Roo.each(_this.toolbarItems, function(e) {
27376 _this.navgroup.addItem(e);
27380 this.first = this.navgroup.addItem({
27381 tooltip: this.firstText,
27382 cls: "prev btn-outline-secondary",
27383 html : ' <i class="fa fa-step-backward"></i>',
27385 preventDefault: true,
27386 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27389 this.prev = this.navgroup.addItem({
27390 tooltip: this.prevText,
27391 cls: "prev btn-outline-secondary",
27392 html : ' <i class="fa fa-backward"></i>',
27394 preventDefault: true,
27395 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27397 //this.addSeparator();
27400 var field = this.navgroup.addItem( {
27402 cls : 'x-paging-position btn-outline-secondary',
27404 html : this.beforePageText +
27405 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27406 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27409 this.field = field.el.select('input', true).first();
27410 this.field.on("keydown", this.onPagingKeydown, this);
27411 this.field.on("focus", function(){this.dom.select();});
27414 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27415 //this.field.setHeight(18);
27416 //this.addSeparator();
27417 this.next = this.navgroup.addItem({
27418 tooltip: this.nextText,
27419 cls: "next btn-outline-secondary",
27420 html : ' <i class="fa fa-forward"></i>',
27422 preventDefault: true,
27423 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27425 this.last = this.navgroup.addItem({
27426 tooltip: this.lastText,
27427 html : ' <i class="fa fa-step-forward"></i>',
27428 cls: "next btn-outline-secondary",
27430 preventDefault: true,
27431 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27433 //this.addSeparator();
27434 this.loading = this.navgroup.addItem({
27435 tooltip: this.refreshText,
27436 cls: "btn-outline-secondary",
27437 html : ' <i class="fa fa-refresh"></i>',
27438 preventDefault: true,
27439 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27445 updateInfo : function(){
27446 if(this.displayEl){
27447 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27448 var msg = count == 0 ?
27452 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27454 this.displayEl.update(msg);
27459 onLoad : function(ds, r, o)
27461 this.cursor = o.params && o.params.start ? o.params.start : 0;
27463 var d = this.getPageData(),
27468 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27469 this.field.dom.value = ap;
27470 this.first.setDisabled(ap == 1);
27471 this.prev.setDisabled(ap == 1);
27472 this.next.setDisabled(ap == ps);
27473 this.last.setDisabled(ap == ps);
27474 this.loading.enable();
27479 getPageData : function(){
27480 var total = this.ds.getTotalCount();
27483 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27484 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27489 onLoadError : function(){
27490 this.loading.enable();
27494 onPagingKeydown : function(e){
27495 var k = e.getKey();
27496 var d = this.getPageData();
27498 var v = this.field.dom.value, pageNum;
27499 if(!v || isNaN(pageNum = parseInt(v, 10))){
27500 this.field.dom.value = d.activePage;
27503 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27504 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27507 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))
27509 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27510 this.field.dom.value = pageNum;
27511 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27514 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27516 var v = this.field.dom.value, pageNum;
27517 var increment = (e.shiftKey) ? 10 : 1;
27518 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27521 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27522 this.field.dom.value = d.activePage;
27525 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27527 this.field.dom.value = parseInt(v, 10) + increment;
27528 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27529 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27536 beforeLoad : function(){
27538 this.loading.disable();
27543 onClick : function(which){
27552 ds.load({params:{start: 0, limit: this.pageSize}});
27555 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27558 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27561 var total = ds.getTotalCount();
27562 var extra = total % this.pageSize;
27563 var lastStart = extra ? (total - extra) : total-this.pageSize;
27564 ds.load({params:{start: lastStart, limit: this.pageSize}});
27567 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27573 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27574 * @param {Roo.data.Store} store The data store to unbind
27576 unbind : function(ds){
27577 ds.un("beforeload", this.beforeLoad, this);
27578 ds.un("load", this.onLoad, this);
27579 ds.un("loadexception", this.onLoadError, this);
27580 ds.un("remove", this.updateInfo, this);
27581 ds.un("add", this.updateInfo, this);
27582 this.ds = undefined;
27586 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27587 * @param {Roo.data.Store} store The data store to bind
27589 bind : function(ds){
27590 ds.on("beforeload", this.beforeLoad, this);
27591 ds.on("load", this.onLoad, this);
27592 ds.on("loadexception", this.onLoadError, this);
27593 ds.on("remove", this.updateInfo, this);
27594 ds.on("add", this.updateInfo, this);
27605 * @class Roo.bootstrap.MessageBar
27606 * @extends Roo.bootstrap.Component
27607 * Bootstrap MessageBar class
27608 * @cfg {String} html contents of the MessageBar
27609 * @cfg {String} weight (info | success | warning | danger) default info
27610 * @cfg {String} beforeClass insert the bar before the given class
27611 * @cfg {Boolean} closable (true | false) default false
27612 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27615 * Create a new Element
27616 * @param {Object} config The config object
27619 Roo.bootstrap.MessageBar = function(config){
27620 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27623 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27629 beforeClass: 'bootstrap-sticky-wrap',
27631 getAutoCreate : function(){
27635 cls: 'alert alert-dismissable alert-' + this.weight,
27640 html: this.html || ''
27646 cfg.cls += ' alert-messages-fixed';
27660 onRender : function(ct, position)
27662 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27665 var cfg = Roo.apply({}, this.getAutoCreate());
27669 cfg.cls += ' ' + this.cls;
27672 cfg.style = this.style;
27674 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27676 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27679 this.el.select('>button.close').on('click', this.hide, this);
27685 if (!this.rendered) {
27691 this.fireEvent('show', this);
27697 if (!this.rendered) {
27703 this.fireEvent('hide', this);
27706 update : function()
27708 // var e = this.el.dom.firstChild;
27710 // if(this.closable){
27711 // e = e.nextSibling;
27714 // e.data = this.html || '';
27716 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27732 * @class Roo.bootstrap.Graph
27733 * @extends Roo.bootstrap.Component
27734 * Bootstrap Graph class
27738 @cfg {String} graphtype bar | vbar | pie
27739 @cfg {number} g_x coodinator | centre x (pie)
27740 @cfg {number} g_y coodinator | centre y (pie)
27741 @cfg {number} g_r radius (pie)
27742 @cfg {number} g_height height of the chart (respected by all elements in the set)
27743 @cfg {number} g_width width of the chart (respected by all elements in the set)
27744 @cfg {Object} title The title of the chart
27747 -opts (object) options for the chart
27749 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27750 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27752 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.
27753 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27755 o stretch (boolean)
27757 -opts (object) options for the pie
27760 o startAngle (number)
27761 o endAngle (number)
27765 * Create a new Input
27766 * @param {Object} config The config object
27769 Roo.bootstrap.Graph = function(config){
27770 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27776 * The img click event for the img.
27777 * @param {Roo.EventObject} e
27783 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27794 //g_colors: this.colors,
27801 getAutoCreate : function(){
27812 onRender : function(ct,position){
27815 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27817 if (typeof(Raphael) == 'undefined') {
27818 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27822 this.raphael = Raphael(this.el.dom);
27824 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27825 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27826 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27827 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27829 r.text(160, 10, "Single Series Chart").attr(txtattr);
27830 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27831 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27832 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27834 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27835 r.barchart(330, 10, 300, 220, data1);
27836 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27837 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27840 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27841 // r.barchart(30, 30, 560, 250, xdata, {
27842 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27843 // axis : "0 0 1 1",
27844 // axisxlabels : xdata
27845 // //yvalues : cols,
27848 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27850 // this.load(null,xdata,{
27851 // axis : "0 0 1 1",
27852 // axisxlabels : xdata
27857 load : function(graphtype,xdata,opts)
27859 this.raphael.clear();
27861 graphtype = this.graphtype;
27866 var r = this.raphael,
27867 fin = function () {
27868 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27870 fout = function () {
27871 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27873 pfin = function() {
27874 this.sector.stop();
27875 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27878 this.label[0].stop();
27879 this.label[0].attr({ r: 7.5 });
27880 this.label[1].attr({ "font-weight": 800 });
27883 pfout = function() {
27884 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27887 this.label[0].animate({ r: 5 }, 500, "bounce");
27888 this.label[1].attr({ "font-weight": 400 });
27894 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27897 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27900 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27901 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27903 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27910 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27915 setTitle: function(o)
27920 initEvents: function() {
27923 this.el.on('click', this.onClick, this);
27927 onClick : function(e)
27929 Roo.log('img onclick');
27930 this.fireEvent('click', this, e);
27942 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27945 * @class Roo.bootstrap.dash.NumberBox
27946 * @extends Roo.bootstrap.Component
27947 * Bootstrap NumberBox class
27948 * @cfg {String} headline Box headline
27949 * @cfg {String} content Box content
27950 * @cfg {String} icon Box icon
27951 * @cfg {String} footer Footer text
27952 * @cfg {String} fhref Footer href
27955 * Create a new NumberBox
27956 * @param {Object} config The config object
27960 Roo.bootstrap.dash.NumberBox = function(config){
27961 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27965 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27974 getAutoCreate : function(){
27978 cls : 'small-box ',
27986 cls : 'roo-headline',
27987 html : this.headline
27991 cls : 'roo-content',
27992 html : this.content
28006 cls : 'ion ' + this.icon
28015 cls : 'small-box-footer',
28016 href : this.fhref || '#',
28020 cfg.cn.push(footer);
28027 onRender : function(ct,position){
28028 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28035 setHeadline: function (value)
28037 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28040 setFooter: function (value, href)
28042 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28045 this.el.select('a.small-box-footer',true).first().attr('href', href);
28050 setContent: function (value)
28052 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28055 initEvents: function()
28069 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28072 * @class Roo.bootstrap.dash.TabBox
28073 * @extends Roo.bootstrap.Component
28074 * Bootstrap TabBox class
28075 * @cfg {String} title Title of the TabBox
28076 * @cfg {String} icon Icon of the TabBox
28077 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28078 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28081 * Create a new TabBox
28082 * @param {Object} config The config object
28086 Roo.bootstrap.dash.TabBox = function(config){
28087 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28092 * When a pane is added
28093 * @param {Roo.bootstrap.dash.TabPane} pane
28097 * @event activatepane
28098 * When a pane is activated
28099 * @param {Roo.bootstrap.dash.TabPane} pane
28101 "activatepane" : true
28109 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28114 tabScrollable : false,
28116 getChildContainer : function()
28118 return this.el.select('.tab-content', true).first();
28121 getAutoCreate : function(){
28125 cls: 'pull-left header',
28133 cls: 'fa ' + this.icon
28139 cls: 'nav nav-tabs pull-right',
28145 if(this.tabScrollable){
28152 cls: 'nav nav-tabs pull-right',
28163 cls: 'nav-tabs-custom',
28168 cls: 'tab-content no-padding',
28176 initEvents : function()
28178 //Roo.log('add add pane handler');
28179 this.on('addpane', this.onAddPane, this);
28182 * Updates the box title
28183 * @param {String} html to set the title to.
28185 setTitle : function(value)
28187 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28189 onAddPane : function(pane)
28191 this.panes.push(pane);
28192 //Roo.log('addpane');
28194 // tabs are rendere left to right..
28195 if(!this.showtabs){
28199 var ctr = this.el.select('.nav-tabs', true).first();
28202 var existing = ctr.select('.nav-tab',true);
28203 var qty = existing.getCount();;
28206 var tab = ctr.createChild({
28208 cls : 'nav-tab' + (qty ? '' : ' active'),
28216 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28219 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28221 pane.el.addClass('active');
28226 onTabClick : function(ev,un,ob,pane)
28228 //Roo.log('tab - prev default');
28229 ev.preventDefault();
28232 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28233 pane.tab.addClass('active');
28234 //Roo.log(pane.title);
28235 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28236 // technically we should have a deactivate event.. but maybe add later.
28237 // and it should not de-activate the selected tab...
28238 this.fireEvent('activatepane', pane);
28239 pane.el.addClass('active');
28240 pane.fireEvent('activate');
28245 getActivePane : function()
28248 Roo.each(this.panes, function(p) {
28249 if(p.el.hasClass('active')){
28270 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28272 * @class Roo.bootstrap.TabPane
28273 * @extends Roo.bootstrap.Component
28274 * Bootstrap TabPane class
28275 * @cfg {Boolean} active (false | true) Default false
28276 * @cfg {String} title title of panel
28280 * Create a new TabPane
28281 * @param {Object} config The config object
28284 Roo.bootstrap.dash.TabPane = function(config){
28285 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28291 * When a pane is activated
28292 * @param {Roo.bootstrap.dash.TabPane} pane
28299 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28304 // the tabBox that this is attached to.
28307 getAutoCreate : function()
28315 cfg.cls += ' active';
28320 initEvents : function()
28322 //Roo.log('trigger add pane handler');
28323 this.parent().fireEvent('addpane', this)
28327 * Updates the tab title
28328 * @param {String} html to set the title to.
28330 setTitle: function(str)
28336 this.tab.select('a', true).first().dom.innerHTML = str;
28353 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28356 * @class Roo.bootstrap.menu.Menu
28357 * @extends Roo.bootstrap.Component
28358 * Bootstrap Menu class - container for Menu
28359 * @cfg {String} html Text of the menu
28360 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28361 * @cfg {String} icon Font awesome icon
28362 * @cfg {String} pos Menu align to (top | bottom) default bottom
28366 * Create a new Menu
28367 * @param {Object} config The config object
28371 Roo.bootstrap.menu.Menu = function(config){
28372 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28376 * @event beforeshow
28377 * Fires before this menu is displayed
28378 * @param {Roo.bootstrap.menu.Menu} this
28382 * @event beforehide
28383 * Fires before this menu is hidden
28384 * @param {Roo.bootstrap.menu.Menu} this
28389 * Fires after this menu is displayed
28390 * @param {Roo.bootstrap.menu.Menu} this
28395 * Fires after this menu is hidden
28396 * @param {Roo.bootstrap.menu.Menu} this
28401 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28402 * @param {Roo.bootstrap.menu.Menu} this
28403 * @param {Roo.EventObject} e
28410 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28414 weight : 'default',
28419 getChildContainer : function() {
28420 if(this.isSubMenu){
28424 return this.el.select('ul.dropdown-menu', true).first();
28427 getAutoCreate : function()
28432 cls : 'roo-menu-text',
28440 cls : 'fa ' + this.icon
28451 cls : 'dropdown-button btn btn-' + this.weight,
28456 cls : 'dropdown-toggle btn btn-' + this.weight,
28466 cls : 'dropdown-menu'
28472 if(this.pos == 'top'){
28473 cfg.cls += ' dropup';
28476 if(this.isSubMenu){
28479 cls : 'dropdown-menu'
28486 onRender : function(ct, position)
28488 this.isSubMenu = ct.hasClass('dropdown-submenu');
28490 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28493 initEvents : function()
28495 if(this.isSubMenu){
28499 this.hidden = true;
28501 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28502 this.triggerEl.on('click', this.onTriggerPress, this);
28504 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28505 this.buttonEl.on('click', this.onClick, this);
28511 if(this.isSubMenu){
28515 return this.el.select('ul.dropdown-menu', true).first();
28518 onClick : function(e)
28520 this.fireEvent("click", this, e);
28523 onTriggerPress : function(e)
28525 if (this.isVisible()) {
28532 isVisible : function(){
28533 return !this.hidden;
28538 this.fireEvent("beforeshow", this);
28540 this.hidden = false;
28541 this.el.addClass('open');
28543 Roo.get(document).on("mouseup", this.onMouseUp, this);
28545 this.fireEvent("show", this);
28552 this.fireEvent("beforehide", this);
28554 this.hidden = true;
28555 this.el.removeClass('open');
28557 Roo.get(document).un("mouseup", this.onMouseUp);
28559 this.fireEvent("hide", this);
28562 onMouseUp : function()
28576 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28579 * @class Roo.bootstrap.menu.Item
28580 * @extends Roo.bootstrap.Component
28581 * Bootstrap MenuItem class
28582 * @cfg {Boolean} submenu (true | false) default false
28583 * @cfg {String} html text of the item
28584 * @cfg {String} href the link
28585 * @cfg {Boolean} disable (true | false) default false
28586 * @cfg {Boolean} preventDefault (true | false) default true
28587 * @cfg {String} icon Font awesome icon
28588 * @cfg {String} pos Submenu align to (left | right) default right
28592 * Create a new Item
28593 * @param {Object} config The config object
28597 Roo.bootstrap.menu.Item = function(config){
28598 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28602 * Fires when the mouse is hovering over this menu
28603 * @param {Roo.bootstrap.menu.Item} this
28604 * @param {Roo.EventObject} e
28609 * Fires when the mouse exits this menu
28610 * @param {Roo.bootstrap.menu.Item} this
28611 * @param {Roo.EventObject} e
28617 * The raw click event for the entire grid.
28618 * @param {Roo.EventObject} e
28624 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28629 preventDefault: true,
28634 getAutoCreate : function()
28639 cls : 'roo-menu-item-text',
28647 cls : 'fa ' + this.icon
28656 href : this.href || '#',
28663 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28667 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28669 if(this.pos == 'left'){
28670 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28677 initEvents : function()
28679 this.el.on('mouseover', this.onMouseOver, this);
28680 this.el.on('mouseout', this.onMouseOut, this);
28682 this.el.select('a', true).first().on('click', this.onClick, this);
28686 onClick : function(e)
28688 if(this.preventDefault){
28689 e.preventDefault();
28692 this.fireEvent("click", this, e);
28695 onMouseOver : function(e)
28697 if(this.submenu && this.pos == 'left'){
28698 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28701 this.fireEvent("mouseover", this, e);
28704 onMouseOut : function(e)
28706 this.fireEvent("mouseout", this, e);
28718 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28721 * @class Roo.bootstrap.menu.Separator
28722 * @extends Roo.bootstrap.Component
28723 * Bootstrap Separator class
28726 * Create a new Separator
28727 * @param {Object} config The config object
28731 Roo.bootstrap.menu.Separator = function(config){
28732 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28735 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28737 getAutoCreate : function(){
28758 * @class Roo.bootstrap.Tooltip
28759 * Bootstrap Tooltip class
28760 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28761 * to determine which dom element triggers the tooltip.
28763 * It needs to add support for additional attributes like tooltip-position
28766 * Create a new Toolti
28767 * @param {Object} config The config object
28770 Roo.bootstrap.Tooltip = function(config){
28771 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28773 this.alignment = Roo.bootstrap.Tooltip.alignment;
28775 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28776 this.alignment = config.alignment;
28781 Roo.apply(Roo.bootstrap.Tooltip, {
28783 * @function init initialize tooltip monitoring.
28787 currentTip : false,
28788 currentRegion : false,
28794 Roo.get(document).on('mouseover', this.enter ,this);
28795 Roo.get(document).on('mouseout', this.leave, this);
28798 this.currentTip = new Roo.bootstrap.Tooltip();
28801 enter : function(ev)
28803 var dom = ev.getTarget();
28805 //Roo.log(['enter',dom]);
28806 var el = Roo.fly(dom);
28807 if (this.currentEl) {
28809 //Roo.log(this.currentEl);
28810 //Roo.log(this.currentEl.contains(dom));
28811 if (this.currentEl == el) {
28814 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28820 if (this.currentTip.el) {
28821 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28825 if(!el || el.dom == document){
28831 // you can not look for children, as if el is the body.. then everythign is the child..
28832 if (!el.attr('tooltip')) { //
28833 if (!el.select("[tooltip]").elements.length) {
28836 // is the mouse over this child...?
28837 bindEl = el.select("[tooltip]").first();
28838 var xy = ev.getXY();
28839 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28840 //Roo.log("not in region.");
28843 //Roo.log("child element over..");
28846 this.currentEl = bindEl;
28847 this.currentTip.bind(bindEl);
28848 this.currentRegion = Roo.lib.Region.getRegion(dom);
28849 this.currentTip.enter();
28852 leave : function(ev)
28854 var dom = ev.getTarget();
28855 //Roo.log(['leave',dom]);
28856 if (!this.currentEl) {
28861 if (dom != this.currentEl.dom) {
28864 var xy = ev.getXY();
28865 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28868 // only activate leave if mouse cursor is outside... bounding box..
28873 if (this.currentTip) {
28874 this.currentTip.leave();
28876 //Roo.log('clear currentEl');
28877 this.currentEl = false;
28882 'left' : ['r-l', [-2,0], 'right'],
28883 'right' : ['l-r', [2,0], 'left'],
28884 'bottom' : ['t-b', [0,2], 'top'],
28885 'top' : [ 'b-t', [0,-2], 'bottom']
28891 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28896 delay : null, // can be { show : 300 , hide: 500}
28900 hoverState : null, //???
28902 placement : 'bottom',
28906 getAutoCreate : function(){
28913 cls : 'tooltip-arrow arrow'
28916 cls : 'tooltip-inner'
28923 bind : function(el)
28928 initEvents : function()
28930 this.arrowEl = this.el.select('.arrow', true).first();
28931 this.innerEl = this.el.select('.tooltip-inner', true).first();
28934 enter : function () {
28936 if (this.timeout != null) {
28937 clearTimeout(this.timeout);
28940 this.hoverState = 'in';
28941 //Roo.log("enter - show");
28942 if (!this.delay || !this.delay.show) {
28947 this.timeout = setTimeout(function () {
28948 if (_t.hoverState == 'in') {
28951 }, this.delay.show);
28955 clearTimeout(this.timeout);
28957 this.hoverState = 'out';
28958 if (!this.delay || !this.delay.hide) {
28964 this.timeout = setTimeout(function () {
28965 //Roo.log("leave - timeout");
28967 if (_t.hoverState == 'out') {
28969 Roo.bootstrap.Tooltip.currentEl = false;
28974 show : function (msg)
28977 this.render(document.body);
28980 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28982 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28984 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28986 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28987 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28989 var placement = typeof this.placement == 'function' ?
28990 this.placement.call(this, this.el, on_el) :
28993 var autoToken = /\s?auto?\s?/i;
28994 var autoPlace = autoToken.test(placement);
28996 placement = placement.replace(autoToken, '') || 'top';
29000 //this.el.setXY([0,0]);
29002 //this.el.dom.style.display='block';
29004 //this.el.appendTo(on_el);
29006 var p = this.getPosition();
29007 var box = this.el.getBox();
29013 var align = this.alignment[placement];
29015 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29017 if(placement == 'top' || placement == 'bottom'){
29019 placement = 'right';
29022 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29023 placement = 'left';
29026 var scroll = Roo.select('body', true).first().getScroll();
29028 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29032 align = this.alignment[placement];
29034 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29038 this.el.alignTo(this.bindEl, align[0],align[1]);
29039 //var arrow = this.el.select('.arrow',true).first();
29040 //arrow.set(align[2],
29042 this.el.addClass(placement);
29043 this.el.addClass("bs-tooltip-"+ placement);
29045 this.el.addClass('in fade show');
29047 this.hoverState = null;
29049 if (this.el.hasClass('fade')) {
29064 //this.el.setXY([0,0]);
29065 this.el.removeClass(['show', 'in']);
29081 * @class Roo.bootstrap.LocationPicker
29082 * @extends Roo.bootstrap.Component
29083 * Bootstrap LocationPicker class
29084 * @cfg {Number} latitude Position when init default 0
29085 * @cfg {Number} longitude Position when init default 0
29086 * @cfg {Number} zoom default 15
29087 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29088 * @cfg {Boolean} mapTypeControl default false
29089 * @cfg {Boolean} disableDoubleClickZoom default false
29090 * @cfg {Boolean} scrollwheel default true
29091 * @cfg {Boolean} streetViewControl default false
29092 * @cfg {Number} radius default 0
29093 * @cfg {String} locationName
29094 * @cfg {Boolean} draggable default true
29095 * @cfg {Boolean} enableAutocomplete default false
29096 * @cfg {Boolean} enableReverseGeocode default true
29097 * @cfg {String} markerTitle
29100 * Create a new LocationPicker
29101 * @param {Object} config The config object
29105 Roo.bootstrap.LocationPicker = function(config){
29107 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29112 * Fires when the picker initialized.
29113 * @param {Roo.bootstrap.LocationPicker} this
29114 * @param {Google Location} location
29118 * @event positionchanged
29119 * Fires when the picker position changed.
29120 * @param {Roo.bootstrap.LocationPicker} this
29121 * @param {Google Location} location
29123 positionchanged : true,
29126 * Fires when the map resize.
29127 * @param {Roo.bootstrap.LocationPicker} this
29132 * Fires when the map show.
29133 * @param {Roo.bootstrap.LocationPicker} this
29138 * Fires when the map hide.
29139 * @param {Roo.bootstrap.LocationPicker} this
29144 * Fires when click the map.
29145 * @param {Roo.bootstrap.LocationPicker} this
29146 * @param {Map event} e
29150 * @event mapRightClick
29151 * Fires when right click the map.
29152 * @param {Roo.bootstrap.LocationPicker} this
29153 * @param {Map event} e
29155 mapRightClick : true,
29157 * @event markerClick
29158 * Fires when click the marker.
29159 * @param {Roo.bootstrap.LocationPicker} this
29160 * @param {Map event} e
29162 markerClick : true,
29164 * @event markerRightClick
29165 * Fires when right click the marker.
29166 * @param {Roo.bootstrap.LocationPicker} this
29167 * @param {Map event} e
29169 markerRightClick : true,
29171 * @event OverlayViewDraw
29172 * Fires when OverlayView Draw
29173 * @param {Roo.bootstrap.LocationPicker} this
29175 OverlayViewDraw : true,
29177 * @event OverlayViewOnAdd
29178 * Fires when OverlayView Draw
29179 * @param {Roo.bootstrap.LocationPicker} this
29181 OverlayViewOnAdd : true,
29183 * @event OverlayViewOnRemove
29184 * Fires when OverlayView Draw
29185 * @param {Roo.bootstrap.LocationPicker} this
29187 OverlayViewOnRemove : true,
29189 * @event OverlayViewShow
29190 * Fires when OverlayView Draw
29191 * @param {Roo.bootstrap.LocationPicker} this
29192 * @param {Pixel} cpx
29194 OverlayViewShow : true,
29196 * @event OverlayViewHide
29197 * Fires when OverlayView Draw
29198 * @param {Roo.bootstrap.LocationPicker} this
29200 OverlayViewHide : true,
29202 * @event loadexception
29203 * Fires when load google lib failed.
29204 * @param {Roo.bootstrap.LocationPicker} this
29206 loadexception : true
29211 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29213 gMapContext: false,
29219 mapTypeControl: false,
29220 disableDoubleClickZoom: false,
29222 streetViewControl: false,
29226 enableAutocomplete: false,
29227 enableReverseGeocode: true,
29230 getAutoCreate: function()
29235 cls: 'roo-location-picker'
29241 initEvents: function(ct, position)
29243 if(!this.el.getWidth() || this.isApplied()){
29247 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29252 initial: function()
29254 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29255 this.fireEvent('loadexception', this);
29259 if(!this.mapTypeId){
29260 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29263 this.gMapContext = this.GMapContext();
29265 this.initOverlayView();
29267 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29271 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29272 _this.setPosition(_this.gMapContext.marker.position);
29275 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29276 _this.fireEvent('mapClick', this, event);
29280 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29281 _this.fireEvent('mapRightClick', this, event);
29285 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29286 _this.fireEvent('markerClick', this, event);
29290 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29291 _this.fireEvent('markerRightClick', this, event);
29295 this.setPosition(this.gMapContext.location);
29297 this.fireEvent('initial', this, this.gMapContext.location);
29300 initOverlayView: function()
29304 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29308 _this.fireEvent('OverlayViewDraw', _this);
29313 _this.fireEvent('OverlayViewOnAdd', _this);
29316 onRemove: function()
29318 _this.fireEvent('OverlayViewOnRemove', _this);
29321 show: function(cpx)
29323 _this.fireEvent('OverlayViewShow', _this, cpx);
29328 _this.fireEvent('OverlayViewHide', _this);
29334 fromLatLngToContainerPixel: function(event)
29336 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29339 isApplied: function()
29341 return this.getGmapContext() == false ? false : true;
29344 getGmapContext: function()
29346 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29349 GMapContext: function()
29351 var position = new google.maps.LatLng(this.latitude, this.longitude);
29353 var _map = new google.maps.Map(this.el.dom, {
29356 mapTypeId: this.mapTypeId,
29357 mapTypeControl: this.mapTypeControl,
29358 disableDoubleClickZoom: this.disableDoubleClickZoom,
29359 scrollwheel: this.scrollwheel,
29360 streetViewControl: this.streetViewControl,
29361 locationName: this.locationName,
29362 draggable: this.draggable,
29363 enableAutocomplete: this.enableAutocomplete,
29364 enableReverseGeocode: this.enableReverseGeocode
29367 var _marker = new google.maps.Marker({
29368 position: position,
29370 title: this.markerTitle,
29371 draggable: this.draggable
29378 location: position,
29379 radius: this.radius,
29380 locationName: this.locationName,
29381 addressComponents: {
29382 formatted_address: null,
29383 addressLine1: null,
29384 addressLine2: null,
29386 streetNumber: null,
29390 stateOrProvince: null
29393 domContainer: this.el.dom,
29394 geodecoder: new google.maps.Geocoder()
29398 drawCircle: function(center, radius, options)
29400 if (this.gMapContext.circle != null) {
29401 this.gMapContext.circle.setMap(null);
29405 options = Roo.apply({}, options, {
29406 strokeColor: "#0000FF",
29407 strokeOpacity: .35,
29409 fillColor: "#0000FF",
29413 options.map = this.gMapContext.map;
29414 options.radius = radius;
29415 options.center = center;
29416 this.gMapContext.circle = new google.maps.Circle(options);
29417 return this.gMapContext.circle;
29423 setPosition: function(location)
29425 this.gMapContext.location = location;
29426 this.gMapContext.marker.setPosition(location);
29427 this.gMapContext.map.panTo(location);
29428 this.drawCircle(location, this.gMapContext.radius, {});
29432 if (this.gMapContext.settings.enableReverseGeocode) {
29433 this.gMapContext.geodecoder.geocode({
29434 latLng: this.gMapContext.location
29435 }, function(results, status) {
29437 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29438 _this.gMapContext.locationName = results[0].formatted_address;
29439 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29441 _this.fireEvent('positionchanged', this, location);
29448 this.fireEvent('positionchanged', this, location);
29453 google.maps.event.trigger(this.gMapContext.map, "resize");
29455 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29457 this.fireEvent('resize', this);
29460 setPositionByLatLng: function(latitude, longitude)
29462 this.setPosition(new google.maps.LatLng(latitude, longitude));
29465 getCurrentPosition: function()
29468 latitude: this.gMapContext.location.lat(),
29469 longitude: this.gMapContext.location.lng()
29473 getAddressName: function()
29475 return this.gMapContext.locationName;
29478 getAddressComponents: function()
29480 return this.gMapContext.addressComponents;
29483 address_component_from_google_geocode: function(address_components)
29487 for (var i = 0; i < address_components.length; i++) {
29488 var component = address_components[i];
29489 if (component.types.indexOf("postal_code") >= 0) {
29490 result.postalCode = component.short_name;
29491 } else if (component.types.indexOf("street_number") >= 0) {
29492 result.streetNumber = component.short_name;
29493 } else if (component.types.indexOf("route") >= 0) {
29494 result.streetName = component.short_name;
29495 } else if (component.types.indexOf("neighborhood") >= 0) {
29496 result.city = component.short_name;
29497 } else if (component.types.indexOf("locality") >= 0) {
29498 result.city = component.short_name;
29499 } else if (component.types.indexOf("sublocality") >= 0) {
29500 result.district = component.short_name;
29501 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29502 result.stateOrProvince = component.short_name;
29503 } else if (component.types.indexOf("country") >= 0) {
29504 result.country = component.short_name;
29508 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29509 result.addressLine2 = "";
29513 setZoomLevel: function(zoom)
29515 this.gMapContext.map.setZoom(zoom);
29528 this.fireEvent('show', this);
29539 this.fireEvent('hide', this);
29544 Roo.apply(Roo.bootstrap.LocationPicker, {
29546 OverlayView : function(map, options)
29548 options = options || {};
29555 * @class Roo.bootstrap.Alert
29556 * @extends Roo.bootstrap.Component
29557 * Bootstrap Alert class - shows an alert area box
29559 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29560 Enter a valid email address
29563 * @cfg {String} title The title of alert
29564 * @cfg {String} html The content of alert
29565 * @cfg {String} weight ( success | info | warning | danger )
29566 * @cfg {String} faicon font-awesomeicon
29569 * Create a new alert
29570 * @param {Object} config The config object
29574 Roo.bootstrap.Alert = function(config){
29575 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29579 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29586 getAutoCreate : function()
29595 cls : 'roo-alert-icon'
29600 cls : 'roo-alert-title',
29605 cls : 'roo-alert-text',
29612 cfg.cn[0].cls += ' fa ' + this.faicon;
29616 cfg.cls += ' alert-' + this.weight;
29622 initEvents: function()
29624 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29627 setTitle : function(str)
29629 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29632 setText : function(str)
29634 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29637 setWeight : function(weight)
29640 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29643 this.weight = weight;
29645 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29648 setIcon : function(icon)
29651 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29654 this.faicon = icon;
29656 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29677 * @class Roo.bootstrap.UploadCropbox
29678 * @extends Roo.bootstrap.Component
29679 * Bootstrap UploadCropbox class
29680 * @cfg {String} emptyText show when image has been loaded
29681 * @cfg {String} rotateNotify show when image too small to rotate
29682 * @cfg {Number} errorTimeout default 3000
29683 * @cfg {Number} minWidth default 300
29684 * @cfg {Number} minHeight default 300
29685 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29686 * @cfg {Boolean} isDocument (true|false) default false
29687 * @cfg {String} url action url
29688 * @cfg {String} paramName default 'imageUpload'
29689 * @cfg {String} method default POST
29690 * @cfg {Boolean} loadMask (true|false) default true
29691 * @cfg {Boolean} loadingText default 'Loading...'
29694 * Create a new UploadCropbox
29695 * @param {Object} config The config object
29698 Roo.bootstrap.UploadCropbox = function(config){
29699 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29703 * @event beforeselectfile
29704 * Fire before select file
29705 * @param {Roo.bootstrap.UploadCropbox} this
29707 "beforeselectfile" : true,
29710 * Fire after initEvent
29711 * @param {Roo.bootstrap.UploadCropbox} this
29716 * Fire after initEvent
29717 * @param {Roo.bootstrap.UploadCropbox} this
29718 * @param {String} data
29723 * Fire when preparing the file data
29724 * @param {Roo.bootstrap.UploadCropbox} this
29725 * @param {Object} file
29730 * Fire when get exception
29731 * @param {Roo.bootstrap.UploadCropbox} this
29732 * @param {XMLHttpRequest} xhr
29734 "exception" : true,
29736 * @event beforeloadcanvas
29737 * Fire before load the canvas
29738 * @param {Roo.bootstrap.UploadCropbox} this
29739 * @param {String} src
29741 "beforeloadcanvas" : true,
29744 * Fire when trash image
29745 * @param {Roo.bootstrap.UploadCropbox} this
29750 * Fire when download the image
29751 * @param {Roo.bootstrap.UploadCropbox} this
29755 * @event footerbuttonclick
29756 * Fire when footerbuttonclick
29757 * @param {Roo.bootstrap.UploadCropbox} this
29758 * @param {String} type
29760 "footerbuttonclick" : true,
29764 * @param {Roo.bootstrap.UploadCropbox} this
29769 * Fire when rotate the image
29770 * @param {Roo.bootstrap.UploadCropbox} this
29771 * @param {String} pos
29776 * Fire when inspect the file
29777 * @param {Roo.bootstrap.UploadCropbox} this
29778 * @param {Object} file
29783 * Fire when xhr upload the file
29784 * @param {Roo.bootstrap.UploadCropbox} this
29785 * @param {Object} data
29790 * Fire when arrange the file data
29791 * @param {Roo.bootstrap.UploadCropbox} this
29792 * @param {Object} formData
29797 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29800 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29802 emptyText : 'Click to upload image',
29803 rotateNotify : 'Image is too small to rotate',
29804 errorTimeout : 3000,
29818 cropType : 'image/jpeg',
29820 canvasLoaded : false,
29821 isDocument : false,
29823 paramName : 'imageUpload',
29825 loadingText : 'Loading...',
29828 getAutoCreate : function()
29832 cls : 'roo-upload-cropbox',
29836 cls : 'roo-upload-cropbox-selector',
29841 cls : 'roo-upload-cropbox-body',
29842 style : 'cursor:pointer',
29846 cls : 'roo-upload-cropbox-preview'
29850 cls : 'roo-upload-cropbox-thumb'
29854 cls : 'roo-upload-cropbox-empty-notify',
29855 html : this.emptyText
29859 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29860 html : this.rotateNotify
29866 cls : 'roo-upload-cropbox-footer',
29869 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29879 onRender : function(ct, position)
29881 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29883 if (this.buttons.length) {
29885 Roo.each(this.buttons, function(bb) {
29887 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29889 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29895 this.maskEl = this.el;
29899 initEvents : function()
29901 this.urlAPI = (window.createObjectURL && window) ||
29902 (window.URL && URL.revokeObjectURL && URL) ||
29903 (window.webkitURL && webkitURL);
29905 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29906 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29908 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29909 this.selectorEl.hide();
29911 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29912 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29914 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29915 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29916 this.thumbEl.hide();
29918 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29919 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29921 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29922 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29923 this.errorEl.hide();
29925 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29926 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29927 this.footerEl.hide();
29929 this.setThumbBoxSize();
29935 this.fireEvent('initial', this);
29942 window.addEventListener("resize", function() { _this.resize(); } );
29944 this.bodyEl.on('click', this.beforeSelectFile, this);
29947 this.bodyEl.on('touchstart', this.onTouchStart, this);
29948 this.bodyEl.on('touchmove', this.onTouchMove, this);
29949 this.bodyEl.on('touchend', this.onTouchEnd, this);
29953 this.bodyEl.on('mousedown', this.onMouseDown, this);
29954 this.bodyEl.on('mousemove', this.onMouseMove, this);
29955 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29956 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29957 Roo.get(document).on('mouseup', this.onMouseUp, this);
29960 this.selectorEl.on('change', this.onFileSelected, this);
29966 this.baseScale = 1;
29968 this.baseRotate = 1;
29969 this.dragable = false;
29970 this.pinching = false;
29973 this.cropData = false;
29974 this.notifyEl.dom.innerHTML = this.emptyText;
29976 this.selectorEl.dom.value = '';
29980 resize : function()
29982 if(this.fireEvent('resize', this) != false){
29983 this.setThumbBoxPosition();
29984 this.setCanvasPosition();
29988 onFooterButtonClick : function(e, el, o, type)
29991 case 'rotate-left' :
29992 this.onRotateLeft(e);
29994 case 'rotate-right' :
29995 this.onRotateRight(e);
29998 this.beforeSelectFile(e);
30013 this.fireEvent('footerbuttonclick', this, type);
30016 beforeSelectFile : function(e)
30018 e.preventDefault();
30020 if(this.fireEvent('beforeselectfile', this) != false){
30021 this.selectorEl.dom.click();
30025 onFileSelected : function(e)
30027 e.preventDefault();
30029 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30033 var file = this.selectorEl.dom.files[0];
30035 if(this.fireEvent('inspect', this, file) != false){
30036 this.prepare(file);
30041 trash : function(e)
30043 this.fireEvent('trash', this);
30046 download : function(e)
30048 this.fireEvent('download', this);
30051 loadCanvas : function(src)
30053 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30057 this.imageEl = document.createElement('img');
30061 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30063 this.imageEl.src = src;
30067 onLoadCanvas : function()
30069 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30070 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30072 this.bodyEl.un('click', this.beforeSelectFile, this);
30074 this.notifyEl.hide();
30075 this.thumbEl.show();
30076 this.footerEl.show();
30078 this.baseRotateLevel();
30080 if(this.isDocument){
30081 this.setThumbBoxSize();
30084 this.setThumbBoxPosition();
30086 this.baseScaleLevel();
30092 this.canvasLoaded = true;
30095 this.maskEl.unmask();
30100 setCanvasPosition : function()
30102 if(!this.canvasEl){
30106 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30107 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30109 this.previewEl.setLeft(pw);
30110 this.previewEl.setTop(ph);
30114 onMouseDown : function(e)
30118 this.dragable = true;
30119 this.pinching = false;
30121 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30122 this.dragable = false;
30126 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30127 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30131 onMouseMove : function(e)
30135 if(!this.canvasLoaded){
30139 if (!this.dragable){
30143 var minX = Math.ceil(this.thumbEl.getLeft(true));
30144 var minY = Math.ceil(this.thumbEl.getTop(true));
30146 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30147 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30149 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30150 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30152 x = x - this.mouseX;
30153 y = y - this.mouseY;
30155 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30156 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30158 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30159 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30161 this.previewEl.setLeft(bgX);
30162 this.previewEl.setTop(bgY);
30164 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30165 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30168 onMouseUp : function(e)
30172 this.dragable = false;
30175 onMouseWheel : function(e)
30179 this.startScale = this.scale;
30181 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30183 if(!this.zoomable()){
30184 this.scale = this.startScale;
30193 zoomable : function()
30195 var minScale = this.thumbEl.getWidth() / this.minWidth;
30197 if(this.minWidth < this.minHeight){
30198 minScale = this.thumbEl.getHeight() / this.minHeight;
30201 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30202 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30206 (this.rotate == 0 || this.rotate == 180) &&
30208 width > this.imageEl.OriginWidth ||
30209 height > this.imageEl.OriginHeight ||
30210 (width < this.minWidth && height < this.minHeight)
30218 (this.rotate == 90 || this.rotate == 270) &&
30220 width > this.imageEl.OriginWidth ||
30221 height > this.imageEl.OriginHeight ||
30222 (width < this.minHeight && height < this.minWidth)
30229 !this.isDocument &&
30230 (this.rotate == 0 || this.rotate == 180) &&
30232 width < this.minWidth ||
30233 width > this.imageEl.OriginWidth ||
30234 height < this.minHeight ||
30235 height > this.imageEl.OriginHeight
30242 !this.isDocument &&
30243 (this.rotate == 90 || this.rotate == 270) &&
30245 width < this.minHeight ||
30246 width > this.imageEl.OriginWidth ||
30247 height < this.minWidth ||
30248 height > this.imageEl.OriginHeight
30258 onRotateLeft : function(e)
30260 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30262 var minScale = this.thumbEl.getWidth() / this.minWidth;
30264 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30265 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30267 this.startScale = this.scale;
30269 while (this.getScaleLevel() < minScale){
30271 this.scale = this.scale + 1;
30273 if(!this.zoomable()){
30278 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30279 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30284 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30291 this.scale = this.startScale;
30293 this.onRotateFail();
30298 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30300 if(this.isDocument){
30301 this.setThumbBoxSize();
30302 this.setThumbBoxPosition();
30303 this.setCanvasPosition();
30308 this.fireEvent('rotate', this, 'left');
30312 onRotateRight : function(e)
30314 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30316 var minScale = this.thumbEl.getWidth() / this.minWidth;
30318 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30319 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30321 this.startScale = this.scale;
30323 while (this.getScaleLevel() < minScale){
30325 this.scale = this.scale + 1;
30327 if(!this.zoomable()){
30332 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30333 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30338 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30345 this.scale = this.startScale;
30347 this.onRotateFail();
30352 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30354 if(this.isDocument){
30355 this.setThumbBoxSize();
30356 this.setThumbBoxPosition();
30357 this.setCanvasPosition();
30362 this.fireEvent('rotate', this, 'right');
30365 onRotateFail : function()
30367 this.errorEl.show(true);
30371 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30376 this.previewEl.dom.innerHTML = '';
30378 var canvasEl = document.createElement("canvas");
30380 var contextEl = canvasEl.getContext("2d");
30382 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30383 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30384 var center = this.imageEl.OriginWidth / 2;
30386 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30387 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30388 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30389 center = this.imageEl.OriginHeight / 2;
30392 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30394 contextEl.translate(center, center);
30395 contextEl.rotate(this.rotate * Math.PI / 180);
30397 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30399 this.canvasEl = document.createElement("canvas");
30401 this.contextEl = this.canvasEl.getContext("2d");
30403 switch (this.rotate) {
30406 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30407 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30409 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30414 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30415 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30417 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30418 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);
30422 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30427 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30428 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30430 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30431 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);
30435 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);
30440 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30441 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30443 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30444 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30448 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);
30455 this.previewEl.appendChild(this.canvasEl);
30457 this.setCanvasPosition();
30462 if(!this.canvasLoaded){
30466 var imageCanvas = document.createElement("canvas");
30468 var imageContext = imageCanvas.getContext("2d");
30470 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30471 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30473 var center = imageCanvas.width / 2;
30475 imageContext.translate(center, center);
30477 imageContext.rotate(this.rotate * Math.PI / 180);
30479 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30481 var canvas = document.createElement("canvas");
30483 var context = canvas.getContext("2d");
30485 canvas.width = this.minWidth;
30486 canvas.height = this.minHeight;
30488 switch (this.rotate) {
30491 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30492 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30494 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30495 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30497 var targetWidth = this.minWidth - 2 * x;
30498 var targetHeight = this.minHeight - 2 * y;
30502 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30503 scale = targetWidth / width;
30506 if(x > 0 && y == 0){
30507 scale = targetHeight / height;
30510 if(x > 0 && y > 0){
30511 scale = targetWidth / width;
30513 if(width < height){
30514 scale = targetHeight / height;
30518 context.scale(scale, scale);
30520 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30521 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30523 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30524 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30526 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30531 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30532 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30534 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30535 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30537 var targetWidth = this.minWidth - 2 * x;
30538 var targetHeight = this.minHeight - 2 * y;
30542 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30543 scale = targetWidth / width;
30546 if(x > 0 && y == 0){
30547 scale = targetHeight / height;
30550 if(x > 0 && y > 0){
30551 scale = targetWidth / width;
30553 if(width < height){
30554 scale = targetHeight / height;
30558 context.scale(scale, scale);
30560 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30561 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30563 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30564 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30566 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30568 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30573 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30574 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30576 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30577 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30579 var targetWidth = this.minWidth - 2 * x;
30580 var targetHeight = this.minHeight - 2 * y;
30584 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30585 scale = targetWidth / width;
30588 if(x > 0 && y == 0){
30589 scale = targetHeight / height;
30592 if(x > 0 && y > 0){
30593 scale = targetWidth / width;
30595 if(width < height){
30596 scale = targetHeight / height;
30600 context.scale(scale, scale);
30602 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30603 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30605 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30606 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30608 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30609 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30611 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30616 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30617 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30619 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30620 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30622 var targetWidth = this.minWidth - 2 * x;
30623 var targetHeight = this.minHeight - 2 * y;
30627 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30628 scale = targetWidth / width;
30631 if(x > 0 && y == 0){
30632 scale = targetHeight / height;
30635 if(x > 0 && y > 0){
30636 scale = targetWidth / width;
30638 if(width < height){
30639 scale = targetHeight / height;
30643 context.scale(scale, scale);
30645 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30646 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30648 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30649 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30651 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30653 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30660 this.cropData = canvas.toDataURL(this.cropType);
30662 if(this.fireEvent('crop', this, this.cropData) !== false){
30663 this.process(this.file, this.cropData);
30670 setThumbBoxSize : function()
30674 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30675 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30676 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30678 this.minWidth = width;
30679 this.minHeight = height;
30681 if(this.rotate == 90 || this.rotate == 270){
30682 this.minWidth = height;
30683 this.minHeight = width;
30688 width = Math.ceil(this.minWidth * height / this.minHeight);
30690 if(this.minWidth > this.minHeight){
30692 height = Math.ceil(this.minHeight * width / this.minWidth);
30695 this.thumbEl.setStyle({
30696 width : width + 'px',
30697 height : height + 'px'
30704 setThumbBoxPosition : function()
30706 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30707 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30709 this.thumbEl.setLeft(x);
30710 this.thumbEl.setTop(y);
30714 baseRotateLevel : function()
30716 this.baseRotate = 1;
30719 typeof(this.exif) != 'undefined' &&
30720 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30721 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30723 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30726 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30730 baseScaleLevel : function()
30734 if(this.isDocument){
30736 if(this.baseRotate == 6 || this.baseRotate == 8){
30738 height = this.thumbEl.getHeight();
30739 this.baseScale = height / this.imageEl.OriginWidth;
30741 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30742 width = this.thumbEl.getWidth();
30743 this.baseScale = width / this.imageEl.OriginHeight;
30749 height = this.thumbEl.getHeight();
30750 this.baseScale = height / this.imageEl.OriginHeight;
30752 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30753 width = this.thumbEl.getWidth();
30754 this.baseScale = width / this.imageEl.OriginWidth;
30760 if(this.baseRotate == 6 || this.baseRotate == 8){
30762 width = this.thumbEl.getHeight();
30763 this.baseScale = width / this.imageEl.OriginHeight;
30765 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30766 height = this.thumbEl.getWidth();
30767 this.baseScale = height / this.imageEl.OriginHeight;
30770 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30771 height = this.thumbEl.getWidth();
30772 this.baseScale = height / this.imageEl.OriginHeight;
30774 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30775 width = this.thumbEl.getHeight();
30776 this.baseScale = width / this.imageEl.OriginWidth;
30783 width = this.thumbEl.getWidth();
30784 this.baseScale = width / this.imageEl.OriginWidth;
30786 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30787 height = this.thumbEl.getHeight();
30788 this.baseScale = height / this.imageEl.OriginHeight;
30791 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30793 height = this.thumbEl.getHeight();
30794 this.baseScale = height / this.imageEl.OriginHeight;
30796 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30797 width = this.thumbEl.getWidth();
30798 this.baseScale = width / this.imageEl.OriginWidth;
30806 getScaleLevel : function()
30808 return this.baseScale * Math.pow(1.1, this.scale);
30811 onTouchStart : function(e)
30813 if(!this.canvasLoaded){
30814 this.beforeSelectFile(e);
30818 var touches = e.browserEvent.touches;
30824 if(touches.length == 1){
30825 this.onMouseDown(e);
30829 if(touches.length != 2){
30835 for(var i = 0, finger; finger = touches[i]; i++){
30836 coords.push(finger.pageX, finger.pageY);
30839 var x = Math.pow(coords[0] - coords[2], 2);
30840 var y = Math.pow(coords[1] - coords[3], 2);
30842 this.startDistance = Math.sqrt(x + y);
30844 this.startScale = this.scale;
30846 this.pinching = true;
30847 this.dragable = false;
30851 onTouchMove : function(e)
30853 if(!this.pinching && !this.dragable){
30857 var touches = e.browserEvent.touches;
30864 this.onMouseMove(e);
30870 for(var i = 0, finger; finger = touches[i]; i++){
30871 coords.push(finger.pageX, finger.pageY);
30874 var x = Math.pow(coords[0] - coords[2], 2);
30875 var y = Math.pow(coords[1] - coords[3], 2);
30877 this.endDistance = Math.sqrt(x + y);
30879 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30881 if(!this.zoomable()){
30882 this.scale = this.startScale;
30890 onTouchEnd : function(e)
30892 this.pinching = false;
30893 this.dragable = false;
30897 process : function(file, crop)
30900 this.maskEl.mask(this.loadingText);
30903 this.xhr = new XMLHttpRequest();
30905 file.xhr = this.xhr;
30907 this.xhr.open(this.method, this.url, true);
30910 "Accept": "application/json",
30911 "Cache-Control": "no-cache",
30912 "X-Requested-With": "XMLHttpRequest"
30915 for (var headerName in headers) {
30916 var headerValue = headers[headerName];
30918 this.xhr.setRequestHeader(headerName, headerValue);
30924 this.xhr.onload = function()
30926 _this.xhrOnLoad(_this.xhr);
30929 this.xhr.onerror = function()
30931 _this.xhrOnError(_this.xhr);
30934 var formData = new FormData();
30936 formData.append('returnHTML', 'NO');
30939 formData.append('crop', crop);
30942 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30943 formData.append(this.paramName, file, file.name);
30946 if(typeof(file.filename) != 'undefined'){
30947 formData.append('filename', file.filename);
30950 if(typeof(file.mimetype) != 'undefined'){
30951 formData.append('mimetype', file.mimetype);
30954 if(this.fireEvent('arrange', this, formData) != false){
30955 this.xhr.send(formData);
30959 xhrOnLoad : function(xhr)
30962 this.maskEl.unmask();
30965 if (xhr.readyState !== 4) {
30966 this.fireEvent('exception', this, xhr);
30970 var response = Roo.decode(xhr.responseText);
30972 if(!response.success){
30973 this.fireEvent('exception', this, xhr);
30977 var response = Roo.decode(xhr.responseText);
30979 this.fireEvent('upload', this, response);
30983 xhrOnError : function()
30986 this.maskEl.unmask();
30989 Roo.log('xhr on error');
30991 var response = Roo.decode(xhr.responseText);
30997 prepare : function(file)
31000 this.maskEl.mask(this.loadingText);
31006 if(typeof(file) === 'string'){
31007 this.loadCanvas(file);
31011 if(!file || !this.urlAPI){
31016 this.cropType = file.type;
31020 if(this.fireEvent('prepare', this, this.file) != false){
31022 var reader = new FileReader();
31024 reader.onload = function (e) {
31025 if (e.target.error) {
31026 Roo.log(e.target.error);
31030 var buffer = e.target.result,
31031 dataView = new DataView(buffer),
31033 maxOffset = dataView.byteLength - 4,
31037 if (dataView.getUint16(0) === 0xffd8) {
31038 while (offset < maxOffset) {
31039 markerBytes = dataView.getUint16(offset);
31041 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31042 markerLength = dataView.getUint16(offset + 2) + 2;
31043 if (offset + markerLength > dataView.byteLength) {
31044 Roo.log('Invalid meta data: Invalid segment size.');
31048 if(markerBytes == 0xffe1){
31049 _this.parseExifData(
31056 offset += markerLength;
31066 var url = _this.urlAPI.createObjectURL(_this.file);
31068 _this.loadCanvas(url);
31073 reader.readAsArrayBuffer(this.file);
31079 parseExifData : function(dataView, offset, length)
31081 var tiffOffset = offset + 10,
31085 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31086 // No Exif data, might be XMP data instead
31090 // Check for the ASCII code for "Exif" (0x45786966):
31091 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31092 // No Exif data, might be XMP data instead
31095 if (tiffOffset + 8 > dataView.byteLength) {
31096 Roo.log('Invalid Exif data: Invalid segment size.');
31099 // Check for the two null bytes:
31100 if (dataView.getUint16(offset + 8) !== 0x0000) {
31101 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31104 // Check the byte alignment:
31105 switch (dataView.getUint16(tiffOffset)) {
31107 littleEndian = true;
31110 littleEndian = false;
31113 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31116 // Check for the TIFF tag marker (0x002A):
31117 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31118 Roo.log('Invalid Exif data: Missing TIFF marker.');
31121 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31122 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31124 this.parseExifTags(
31127 tiffOffset + dirOffset,
31132 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31137 if (dirOffset + 6 > dataView.byteLength) {
31138 Roo.log('Invalid Exif data: Invalid directory offset.');
31141 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31142 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31143 if (dirEndOffset + 4 > dataView.byteLength) {
31144 Roo.log('Invalid Exif data: Invalid directory size.');
31147 for (i = 0; i < tagsNumber; i += 1) {
31151 dirOffset + 2 + 12 * i, // tag offset
31155 // Return the offset to the next directory:
31156 return dataView.getUint32(dirEndOffset, littleEndian);
31159 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31161 var tag = dataView.getUint16(offset, littleEndian);
31163 this.exif[tag] = this.getExifValue(
31167 dataView.getUint16(offset + 2, littleEndian), // tag type
31168 dataView.getUint32(offset + 4, littleEndian), // tag length
31173 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31175 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31184 Roo.log('Invalid Exif data: Invalid tag type.');
31188 tagSize = tagType.size * length;
31189 // Determine if the value is contained in the dataOffset bytes,
31190 // or if the value at the dataOffset is a pointer to the actual data:
31191 dataOffset = tagSize > 4 ?
31192 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31193 if (dataOffset + tagSize > dataView.byteLength) {
31194 Roo.log('Invalid Exif data: Invalid data offset.');
31197 if (length === 1) {
31198 return tagType.getValue(dataView, dataOffset, littleEndian);
31201 for (i = 0; i < length; i += 1) {
31202 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31205 if (tagType.ascii) {
31207 // Concatenate the chars:
31208 for (i = 0; i < values.length; i += 1) {
31210 // Ignore the terminating NULL byte(s):
31211 if (c === '\u0000') {
31223 Roo.apply(Roo.bootstrap.UploadCropbox, {
31225 'Orientation': 0x0112
31229 1: 0, //'top-left',
31231 3: 180, //'bottom-right',
31232 // 4: 'bottom-left',
31234 6: 90, //'right-top',
31235 // 7: 'right-bottom',
31236 8: 270 //'left-bottom'
31240 // byte, 8-bit unsigned int:
31242 getValue: function (dataView, dataOffset) {
31243 return dataView.getUint8(dataOffset);
31247 // ascii, 8-bit byte:
31249 getValue: function (dataView, dataOffset) {
31250 return String.fromCharCode(dataView.getUint8(dataOffset));
31255 // short, 16 bit int:
31257 getValue: function (dataView, dataOffset, littleEndian) {
31258 return dataView.getUint16(dataOffset, littleEndian);
31262 // long, 32 bit int:
31264 getValue: function (dataView, dataOffset, littleEndian) {
31265 return dataView.getUint32(dataOffset, littleEndian);
31269 // rational = two long values, first is numerator, second is denominator:
31271 getValue: function (dataView, dataOffset, littleEndian) {
31272 return dataView.getUint32(dataOffset, littleEndian) /
31273 dataView.getUint32(dataOffset + 4, littleEndian);
31277 // slong, 32 bit signed int:
31279 getValue: function (dataView, dataOffset, littleEndian) {
31280 return dataView.getInt32(dataOffset, littleEndian);
31284 // srational, two slongs, first is numerator, second is denominator:
31286 getValue: function (dataView, dataOffset, littleEndian) {
31287 return dataView.getInt32(dataOffset, littleEndian) /
31288 dataView.getInt32(dataOffset + 4, littleEndian);
31298 cls : 'btn-group roo-upload-cropbox-rotate-left',
31299 action : 'rotate-left',
31303 cls : 'btn btn-default',
31304 html : '<i class="fa fa-undo"></i>'
31310 cls : 'btn-group roo-upload-cropbox-picture',
31311 action : 'picture',
31315 cls : 'btn btn-default',
31316 html : '<i class="fa fa-picture-o"></i>'
31322 cls : 'btn-group roo-upload-cropbox-rotate-right',
31323 action : 'rotate-right',
31327 cls : 'btn btn-default',
31328 html : '<i class="fa fa-repeat"></i>'
31336 cls : 'btn-group roo-upload-cropbox-rotate-left',
31337 action : 'rotate-left',
31341 cls : 'btn btn-default',
31342 html : '<i class="fa fa-undo"></i>'
31348 cls : 'btn-group roo-upload-cropbox-download',
31349 action : 'download',
31353 cls : 'btn btn-default',
31354 html : '<i class="fa fa-download"></i>'
31360 cls : 'btn-group roo-upload-cropbox-crop',
31365 cls : 'btn btn-default',
31366 html : '<i class="fa fa-crop"></i>'
31372 cls : 'btn-group roo-upload-cropbox-trash',
31377 cls : 'btn btn-default',
31378 html : '<i class="fa fa-trash"></i>'
31384 cls : 'btn-group roo-upload-cropbox-rotate-right',
31385 action : 'rotate-right',
31389 cls : 'btn btn-default',
31390 html : '<i class="fa fa-repeat"></i>'
31398 cls : 'btn-group roo-upload-cropbox-rotate-left',
31399 action : 'rotate-left',
31403 cls : 'btn btn-default',
31404 html : '<i class="fa fa-undo"></i>'
31410 cls : 'btn-group roo-upload-cropbox-rotate-right',
31411 action : 'rotate-right',
31415 cls : 'btn btn-default',
31416 html : '<i class="fa fa-repeat"></i>'
31429 * @class Roo.bootstrap.DocumentManager
31430 * @extends Roo.bootstrap.Component
31431 * Bootstrap DocumentManager class
31432 * @cfg {String} paramName default 'imageUpload'
31433 * @cfg {String} toolTipName default 'filename'
31434 * @cfg {String} method default POST
31435 * @cfg {String} url action url
31436 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31437 * @cfg {Boolean} multiple multiple upload default true
31438 * @cfg {Number} thumbSize default 300
31439 * @cfg {String} fieldLabel
31440 * @cfg {Number} labelWidth default 4
31441 * @cfg {String} labelAlign (left|top) default left
31442 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31443 * @cfg {Number} labellg set the width of label (1-12)
31444 * @cfg {Number} labelmd set the width of label (1-12)
31445 * @cfg {Number} labelsm set the width of label (1-12)
31446 * @cfg {Number} labelxs set the width of label (1-12)
31449 * Create a new DocumentManager
31450 * @param {Object} config The config object
31453 Roo.bootstrap.DocumentManager = function(config){
31454 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31457 this.delegates = [];
31462 * Fire when initial the DocumentManager
31463 * @param {Roo.bootstrap.DocumentManager} this
31468 * inspect selected file
31469 * @param {Roo.bootstrap.DocumentManager} this
31470 * @param {File} file
31475 * Fire when xhr load exception
31476 * @param {Roo.bootstrap.DocumentManager} this
31477 * @param {XMLHttpRequest} xhr
31479 "exception" : true,
31481 * @event afterupload
31482 * Fire when xhr load exception
31483 * @param {Roo.bootstrap.DocumentManager} this
31484 * @param {XMLHttpRequest} xhr
31486 "afterupload" : true,
31489 * prepare the form data
31490 * @param {Roo.bootstrap.DocumentManager} this
31491 * @param {Object} formData
31496 * Fire when remove the file
31497 * @param {Roo.bootstrap.DocumentManager} this
31498 * @param {Object} file
31503 * Fire after refresh the file
31504 * @param {Roo.bootstrap.DocumentManager} this
31509 * Fire after click the image
31510 * @param {Roo.bootstrap.DocumentManager} this
31511 * @param {Object} file
31516 * Fire when upload a image and editable set to true
31517 * @param {Roo.bootstrap.DocumentManager} this
31518 * @param {Object} file
31522 * @event beforeselectfile
31523 * Fire before select file
31524 * @param {Roo.bootstrap.DocumentManager} this
31526 "beforeselectfile" : true,
31529 * Fire before process file
31530 * @param {Roo.bootstrap.DocumentManager} this
31531 * @param {Object} file
31535 * @event previewrendered
31536 * Fire when preview rendered
31537 * @param {Roo.bootstrap.DocumentManager} this
31538 * @param {Object} file
31540 "previewrendered" : true,
31543 "previewResize" : true
31548 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31557 paramName : 'imageUpload',
31558 toolTipName : 'filename',
31561 labelAlign : 'left',
31571 getAutoCreate : function()
31573 var managerWidget = {
31575 cls : 'roo-document-manager',
31579 cls : 'roo-document-manager-selector',
31584 cls : 'roo-document-manager-uploader',
31588 cls : 'roo-document-manager-upload-btn',
31589 html : '<i class="fa fa-plus"></i>'
31600 cls : 'column col-md-12',
31605 if(this.fieldLabel.length){
31610 cls : 'column col-md-12',
31611 html : this.fieldLabel
31615 cls : 'column col-md-12',
31620 if(this.labelAlign == 'left'){
31625 html : this.fieldLabel
31634 if(this.labelWidth > 12){
31635 content[0].style = "width: " + this.labelWidth + 'px';
31638 if(this.labelWidth < 13 && this.labelmd == 0){
31639 this.labelmd = this.labelWidth;
31642 if(this.labellg > 0){
31643 content[0].cls += ' col-lg-' + this.labellg;
31644 content[1].cls += ' col-lg-' + (12 - this.labellg);
31647 if(this.labelmd > 0){
31648 content[0].cls += ' col-md-' + this.labelmd;
31649 content[1].cls += ' col-md-' + (12 - this.labelmd);
31652 if(this.labelsm > 0){
31653 content[0].cls += ' col-sm-' + this.labelsm;
31654 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31657 if(this.labelxs > 0){
31658 content[0].cls += ' col-xs-' + this.labelxs;
31659 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31667 cls : 'row clearfix',
31675 initEvents : function()
31677 this.managerEl = this.el.select('.roo-document-manager', true).first();
31678 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31680 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31681 this.selectorEl.hide();
31684 this.selectorEl.attr('multiple', 'multiple');
31687 this.selectorEl.on('change', this.onFileSelected, this);
31689 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31690 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31692 this.uploader.on('click', this.onUploaderClick, this);
31694 this.renderProgressDialog();
31698 window.addEventListener("resize", function() { _this.refresh(); } );
31700 this.fireEvent('initial', this);
31703 renderProgressDialog : function()
31707 this.progressDialog = new Roo.bootstrap.Modal({
31708 cls : 'roo-document-manager-progress-dialog',
31709 allow_close : false,
31720 btnclick : function() {
31721 _this.uploadCancel();
31727 this.progressDialog.render(Roo.get(document.body));
31729 this.progress = new Roo.bootstrap.Progress({
31730 cls : 'roo-document-manager-progress',
31735 this.progress.render(this.progressDialog.getChildContainer());
31737 this.progressBar = new Roo.bootstrap.ProgressBar({
31738 cls : 'roo-document-manager-progress-bar',
31741 aria_valuemax : 12,
31745 this.progressBar.render(this.progress.getChildContainer());
31748 onUploaderClick : function(e)
31750 e.preventDefault();
31752 if(this.fireEvent('beforeselectfile', this) != false){
31753 this.selectorEl.dom.click();
31758 onFileSelected : function(e)
31760 e.preventDefault();
31762 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31766 Roo.each(this.selectorEl.dom.files, function(file){
31767 if(this.fireEvent('inspect', this, file) != false){
31768 this.files.push(file);
31778 this.selectorEl.dom.value = '';
31780 if(!this.files || !this.files.length){
31784 if(this.boxes > 0 && this.files.length > this.boxes){
31785 this.files = this.files.slice(0, this.boxes);
31788 this.uploader.show();
31790 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31791 this.uploader.hide();
31800 Roo.each(this.files, function(file){
31802 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31803 var f = this.renderPreview(file);
31808 if(file.type.indexOf('image') != -1){
31809 this.delegates.push(
31811 _this.process(file);
31812 }).createDelegate(this)
31820 _this.process(file);
31821 }).createDelegate(this)
31826 this.files = files;
31828 this.delegates = this.delegates.concat(docs);
31830 if(!this.delegates.length){
31835 this.progressBar.aria_valuemax = this.delegates.length;
31842 arrange : function()
31844 if(!this.delegates.length){
31845 this.progressDialog.hide();
31850 var delegate = this.delegates.shift();
31852 this.progressDialog.show();
31854 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31856 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31861 refresh : function()
31863 this.uploader.show();
31865 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31866 this.uploader.hide();
31869 Roo.isTouch ? this.closable(false) : this.closable(true);
31871 this.fireEvent('refresh', this);
31874 onRemove : function(e, el, o)
31876 e.preventDefault();
31878 this.fireEvent('remove', this, o);
31882 remove : function(o)
31886 Roo.each(this.files, function(file){
31887 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31896 this.files = files;
31903 Roo.each(this.files, function(file){
31908 file.target.remove();
31917 onClick : function(e, el, o)
31919 e.preventDefault();
31921 this.fireEvent('click', this, o);
31925 closable : function(closable)
31927 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31929 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31941 xhrOnLoad : function(xhr)
31943 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31947 if (xhr.readyState !== 4) {
31949 this.fireEvent('exception', this, xhr);
31953 var response = Roo.decode(xhr.responseText);
31955 if(!response.success){
31957 this.fireEvent('exception', this, xhr);
31961 var file = this.renderPreview(response.data);
31963 this.files.push(file);
31967 this.fireEvent('afterupload', this, xhr);
31971 xhrOnError : function(xhr)
31973 Roo.log('xhr on error');
31975 var response = Roo.decode(xhr.responseText);
31982 process : function(file)
31984 if(this.fireEvent('process', this, file) !== false){
31985 if(this.editable && file.type.indexOf('image') != -1){
31986 this.fireEvent('edit', this, file);
31990 this.uploadStart(file, false);
31997 uploadStart : function(file, crop)
31999 this.xhr = new XMLHttpRequest();
32001 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32006 file.xhr = this.xhr;
32008 this.managerEl.createChild({
32010 cls : 'roo-document-manager-loading',
32014 tooltip : file.name,
32015 cls : 'roo-document-manager-thumb',
32016 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32022 this.xhr.open(this.method, this.url, true);
32025 "Accept": "application/json",
32026 "Cache-Control": "no-cache",
32027 "X-Requested-With": "XMLHttpRequest"
32030 for (var headerName in headers) {
32031 var headerValue = headers[headerName];
32033 this.xhr.setRequestHeader(headerName, headerValue);
32039 this.xhr.onload = function()
32041 _this.xhrOnLoad(_this.xhr);
32044 this.xhr.onerror = function()
32046 _this.xhrOnError(_this.xhr);
32049 var formData = new FormData();
32051 formData.append('returnHTML', 'NO');
32054 formData.append('crop', crop);
32057 formData.append(this.paramName, file, file.name);
32064 if(this.fireEvent('prepare', this, formData, options) != false){
32066 if(options.manually){
32070 this.xhr.send(formData);
32074 this.uploadCancel();
32077 uploadCancel : function()
32083 this.delegates = [];
32085 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32092 renderPreview : function(file)
32094 if(typeof(file.target) != 'undefined' && file.target){
32098 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32100 var previewEl = this.managerEl.createChild({
32102 cls : 'roo-document-manager-preview',
32106 tooltip : file[this.toolTipName],
32107 cls : 'roo-document-manager-thumb',
32108 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32113 html : '<i class="fa fa-times-circle"></i>'
32118 var close = previewEl.select('button.close', true).first();
32120 close.on('click', this.onRemove, this, file);
32122 file.target = previewEl;
32124 var image = previewEl.select('img', true).first();
32128 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32130 image.on('click', this.onClick, this, file);
32132 this.fireEvent('previewrendered', this, file);
32138 onPreviewLoad : function(file, image)
32140 if(typeof(file.target) == 'undefined' || !file.target){
32144 var width = image.dom.naturalWidth || image.dom.width;
32145 var height = image.dom.naturalHeight || image.dom.height;
32147 if(!this.previewResize) {
32151 if(width > height){
32152 file.target.addClass('wide');
32156 file.target.addClass('tall');
32161 uploadFromSource : function(file, crop)
32163 this.xhr = new XMLHttpRequest();
32165 this.managerEl.createChild({
32167 cls : 'roo-document-manager-loading',
32171 tooltip : file.name,
32172 cls : 'roo-document-manager-thumb',
32173 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32179 this.xhr.open(this.method, this.url, true);
32182 "Accept": "application/json",
32183 "Cache-Control": "no-cache",
32184 "X-Requested-With": "XMLHttpRequest"
32187 for (var headerName in headers) {
32188 var headerValue = headers[headerName];
32190 this.xhr.setRequestHeader(headerName, headerValue);
32196 this.xhr.onload = function()
32198 _this.xhrOnLoad(_this.xhr);
32201 this.xhr.onerror = function()
32203 _this.xhrOnError(_this.xhr);
32206 var formData = new FormData();
32208 formData.append('returnHTML', 'NO');
32210 formData.append('crop', crop);
32212 if(typeof(file.filename) != 'undefined'){
32213 formData.append('filename', file.filename);
32216 if(typeof(file.mimetype) != 'undefined'){
32217 formData.append('mimetype', file.mimetype);
32222 if(this.fireEvent('prepare', this, formData) != false){
32223 this.xhr.send(formData);
32233 * @class Roo.bootstrap.DocumentViewer
32234 * @extends Roo.bootstrap.Component
32235 * Bootstrap DocumentViewer class
32236 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32237 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32240 * Create a new DocumentViewer
32241 * @param {Object} config The config object
32244 Roo.bootstrap.DocumentViewer = function(config){
32245 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32250 * Fire after initEvent
32251 * @param {Roo.bootstrap.DocumentViewer} this
32257 * @param {Roo.bootstrap.DocumentViewer} this
32262 * Fire after download button
32263 * @param {Roo.bootstrap.DocumentViewer} this
32268 * Fire after trash button
32269 * @param {Roo.bootstrap.DocumentViewer} this
32276 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32278 showDownload : true,
32282 getAutoCreate : function()
32286 cls : 'roo-document-viewer',
32290 cls : 'roo-document-viewer-body',
32294 cls : 'roo-document-viewer-thumb',
32298 cls : 'roo-document-viewer-image'
32306 cls : 'roo-document-viewer-footer',
32309 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32313 cls : 'btn-group roo-document-viewer-download',
32317 cls : 'btn btn-default',
32318 html : '<i class="fa fa-download"></i>'
32324 cls : 'btn-group roo-document-viewer-trash',
32328 cls : 'btn btn-default',
32329 html : '<i class="fa fa-trash"></i>'
32342 initEvents : function()
32344 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32345 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32347 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32348 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32350 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32351 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32353 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32354 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32356 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32357 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32359 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32360 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32362 this.bodyEl.on('click', this.onClick, this);
32363 this.downloadBtn.on('click', this.onDownload, this);
32364 this.trashBtn.on('click', this.onTrash, this);
32366 this.downloadBtn.hide();
32367 this.trashBtn.hide();
32369 if(this.showDownload){
32370 this.downloadBtn.show();
32373 if(this.showTrash){
32374 this.trashBtn.show();
32377 if(!this.showDownload && !this.showTrash) {
32378 this.footerEl.hide();
32383 initial : function()
32385 this.fireEvent('initial', this);
32389 onClick : function(e)
32391 e.preventDefault();
32393 this.fireEvent('click', this);
32396 onDownload : function(e)
32398 e.preventDefault();
32400 this.fireEvent('download', this);
32403 onTrash : function(e)
32405 e.preventDefault();
32407 this.fireEvent('trash', this);
32419 * @class Roo.bootstrap.NavProgressBar
32420 * @extends Roo.bootstrap.Component
32421 * Bootstrap NavProgressBar class
32424 * Create a new nav progress bar
32425 * @param {Object} config The config object
32428 Roo.bootstrap.NavProgressBar = function(config){
32429 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32431 this.bullets = this.bullets || [];
32433 // Roo.bootstrap.NavProgressBar.register(this);
32437 * Fires when the active item changes
32438 * @param {Roo.bootstrap.NavProgressBar} this
32439 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32440 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32447 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32452 getAutoCreate : function()
32454 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32458 cls : 'roo-navigation-bar-group',
32462 cls : 'roo-navigation-top-bar'
32466 cls : 'roo-navigation-bullets-bar',
32470 cls : 'roo-navigation-bar'
32477 cls : 'roo-navigation-bottom-bar'
32487 initEvents: function()
32492 onRender : function(ct, position)
32494 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32496 if(this.bullets.length){
32497 Roo.each(this.bullets, function(b){
32506 addItem : function(cfg)
32508 var item = new Roo.bootstrap.NavProgressItem(cfg);
32510 item.parentId = this.id;
32511 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32514 var top = new Roo.bootstrap.Element({
32516 cls : 'roo-navigation-bar-text'
32519 var bottom = new Roo.bootstrap.Element({
32521 cls : 'roo-navigation-bar-text'
32524 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32525 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32527 var topText = new Roo.bootstrap.Element({
32529 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32532 var bottomText = new Roo.bootstrap.Element({
32534 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32537 topText.onRender(top.el, null);
32538 bottomText.onRender(bottom.el, null);
32541 item.bottomEl = bottom;
32544 this.barItems.push(item);
32549 getActive : function()
32551 var active = false;
32553 Roo.each(this.barItems, function(v){
32555 if (!v.isActive()) {
32567 setActiveItem : function(item)
32571 Roo.each(this.barItems, function(v){
32572 if (v.rid == item.rid) {
32576 if (v.isActive()) {
32577 v.setActive(false);
32582 item.setActive(true);
32584 this.fireEvent('changed', this, item, prev);
32587 getBarItem: function(rid)
32591 Roo.each(this.barItems, function(e) {
32592 if (e.rid != rid) {
32603 indexOfItem : function(item)
32607 Roo.each(this.barItems, function(v, i){
32609 if (v.rid != item.rid) {
32620 setActiveNext : function()
32622 var i = this.indexOfItem(this.getActive());
32624 if (i > this.barItems.length) {
32628 this.setActiveItem(this.barItems[i+1]);
32631 setActivePrev : function()
32633 var i = this.indexOfItem(this.getActive());
32639 this.setActiveItem(this.barItems[i-1]);
32642 format : function()
32644 if(!this.barItems.length){
32648 var width = 100 / this.barItems.length;
32650 Roo.each(this.barItems, function(i){
32651 i.el.setStyle('width', width + '%');
32652 i.topEl.el.setStyle('width', width + '%');
32653 i.bottomEl.el.setStyle('width', width + '%');
32662 * Nav Progress Item
32667 * @class Roo.bootstrap.NavProgressItem
32668 * @extends Roo.bootstrap.Component
32669 * Bootstrap NavProgressItem class
32670 * @cfg {String} rid the reference id
32671 * @cfg {Boolean} active (true|false) Is item active default false
32672 * @cfg {Boolean} disabled (true|false) Is item active default false
32673 * @cfg {String} html
32674 * @cfg {String} position (top|bottom) text position default bottom
32675 * @cfg {String} icon show icon instead of number
32678 * Create a new NavProgressItem
32679 * @param {Object} config The config object
32681 Roo.bootstrap.NavProgressItem = function(config){
32682 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32687 * The raw click event for the entire grid.
32688 * @param {Roo.bootstrap.NavProgressItem} this
32689 * @param {Roo.EventObject} e
32696 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32702 position : 'bottom',
32705 getAutoCreate : function()
32707 var iconCls = 'roo-navigation-bar-item-icon';
32709 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32713 cls: 'roo-navigation-bar-item',
32723 cfg.cls += ' active';
32726 cfg.cls += ' disabled';
32732 disable : function()
32734 this.setDisabled(true);
32737 enable : function()
32739 this.setDisabled(false);
32742 initEvents: function()
32744 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32746 this.iconEl.on('click', this.onClick, this);
32749 onClick : function(e)
32751 e.preventDefault();
32757 if(this.fireEvent('click', this, e) === false){
32761 this.parent().setActiveItem(this);
32764 isActive: function ()
32766 return this.active;
32769 setActive : function(state)
32771 if(this.active == state){
32775 this.active = state;
32778 this.el.addClass('active');
32782 this.el.removeClass('active');
32787 setDisabled : function(state)
32789 if(this.disabled == state){
32793 this.disabled = state;
32796 this.el.addClass('disabled');
32800 this.el.removeClass('disabled');
32803 tooltipEl : function()
32805 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32818 * @class Roo.bootstrap.FieldLabel
32819 * @extends Roo.bootstrap.Component
32820 * Bootstrap FieldLabel class
32821 * @cfg {String} html contents of the element
32822 * @cfg {String} tag tag of the element default label
32823 * @cfg {String} cls class of the element
32824 * @cfg {String} target label target
32825 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32826 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32827 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32828 * @cfg {String} iconTooltip default "This field is required"
32829 * @cfg {String} indicatorpos (left|right) default left
32832 * Create a new FieldLabel
32833 * @param {Object} config The config object
32836 Roo.bootstrap.FieldLabel = function(config){
32837 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32842 * Fires after the field has been marked as invalid.
32843 * @param {Roo.form.FieldLabel} this
32844 * @param {String} msg The validation message
32849 * Fires after the field has been validated with no errors.
32850 * @param {Roo.form.FieldLabel} this
32856 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32863 invalidClass : 'has-warning',
32864 validClass : 'has-success',
32865 iconTooltip : 'This field is required',
32866 indicatorpos : 'left',
32868 getAutoCreate : function(){
32871 if (!this.allowBlank) {
32877 cls : 'roo-bootstrap-field-label ' + this.cls,
32882 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32883 tooltip : this.iconTooltip
32892 if(this.indicatorpos == 'right'){
32895 cls : 'roo-bootstrap-field-label ' + this.cls,
32904 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32905 tooltip : this.iconTooltip
32914 initEvents: function()
32916 Roo.bootstrap.Element.superclass.initEvents.call(this);
32918 this.indicator = this.indicatorEl();
32920 if(this.indicator){
32921 this.indicator.removeClass('visible');
32922 this.indicator.addClass('invisible');
32925 Roo.bootstrap.FieldLabel.register(this);
32928 indicatorEl : function()
32930 var indicator = this.el.select('i.roo-required-indicator',true).first();
32941 * Mark this field as valid
32943 markValid : function()
32945 if(this.indicator){
32946 this.indicator.removeClass('visible');
32947 this.indicator.addClass('invisible');
32949 if (Roo.bootstrap.version == 3) {
32950 this.el.removeClass(this.invalidClass);
32951 this.el.addClass(this.validClass);
32953 this.el.removeClass('is-invalid');
32954 this.el.addClass('is-valid');
32958 this.fireEvent('valid', this);
32962 * Mark this field as invalid
32963 * @param {String} msg The validation message
32965 markInvalid : function(msg)
32967 if(this.indicator){
32968 this.indicator.removeClass('invisible');
32969 this.indicator.addClass('visible');
32971 if (Roo.bootstrap.version == 3) {
32972 this.el.removeClass(this.validClass);
32973 this.el.addClass(this.invalidClass);
32975 this.el.removeClass('is-valid');
32976 this.el.addClass('is-invalid');
32980 this.fireEvent('invalid', this, msg);
32986 Roo.apply(Roo.bootstrap.FieldLabel, {
32991 * register a FieldLabel Group
32992 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32994 register : function(label)
32996 if(this.groups.hasOwnProperty(label.target)){
33000 this.groups[label.target] = label;
33004 * fetch a FieldLabel Group based on the target
33005 * @param {string} target
33006 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33008 get: function(target) {
33009 if (typeof(this.groups[target]) == 'undefined') {
33013 return this.groups[target] ;
33022 * page DateSplitField.
33028 * @class Roo.bootstrap.DateSplitField
33029 * @extends Roo.bootstrap.Component
33030 * Bootstrap DateSplitField class
33031 * @cfg {string} fieldLabel - the label associated
33032 * @cfg {Number} labelWidth set the width of label (0-12)
33033 * @cfg {String} labelAlign (top|left)
33034 * @cfg {Boolean} dayAllowBlank (true|false) default false
33035 * @cfg {Boolean} monthAllowBlank (true|false) default false
33036 * @cfg {Boolean} yearAllowBlank (true|false) default false
33037 * @cfg {string} dayPlaceholder
33038 * @cfg {string} monthPlaceholder
33039 * @cfg {string} yearPlaceholder
33040 * @cfg {string} dayFormat default 'd'
33041 * @cfg {string} monthFormat default 'm'
33042 * @cfg {string} yearFormat default 'Y'
33043 * @cfg {Number} labellg set the width of label (1-12)
33044 * @cfg {Number} labelmd set the width of label (1-12)
33045 * @cfg {Number} labelsm set the width of label (1-12)
33046 * @cfg {Number} labelxs set the width of label (1-12)
33050 * Create a new DateSplitField
33051 * @param {Object} config The config object
33054 Roo.bootstrap.DateSplitField = function(config){
33055 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33061 * getting the data of years
33062 * @param {Roo.bootstrap.DateSplitField} this
33063 * @param {Object} years
33068 * getting the data of days
33069 * @param {Roo.bootstrap.DateSplitField} this
33070 * @param {Object} days
33075 * Fires after the field has been marked as invalid.
33076 * @param {Roo.form.Field} this
33077 * @param {String} msg The validation message
33082 * Fires after the field has been validated with no errors.
33083 * @param {Roo.form.Field} this
33089 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33092 labelAlign : 'top',
33094 dayAllowBlank : false,
33095 monthAllowBlank : false,
33096 yearAllowBlank : false,
33097 dayPlaceholder : '',
33098 monthPlaceholder : '',
33099 yearPlaceholder : '',
33103 isFormField : true,
33109 getAutoCreate : function()
33113 cls : 'row roo-date-split-field-group',
33118 cls : 'form-hidden-field roo-date-split-field-group-value',
33124 var labelCls = 'col-md-12';
33125 var contentCls = 'col-md-4';
33127 if(this.fieldLabel){
33131 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33135 html : this.fieldLabel
33140 if(this.labelAlign == 'left'){
33142 if(this.labelWidth > 12){
33143 label.style = "width: " + this.labelWidth + 'px';
33146 if(this.labelWidth < 13 && this.labelmd == 0){
33147 this.labelmd = this.labelWidth;
33150 if(this.labellg > 0){
33151 labelCls = ' col-lg-' + this.labellg;
33152 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33155 if(this.labelmd > 0){
33156 labelCls = ' col-md-' + this.labelmd;
33157 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33160 if(this.labelsm > 0){
33161 labelCls = ' col-sm-' + this.labelsm;
33162 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33165 if(this.labelxs > 0){
33166 labelCls = ' col-xs-' + this.labelxs;
33167 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33171 label.cls += ' ' + labelCls;
33173 cfg.cn.push(label);
33176 Roo.each(['day', 'month', 'year'], function(t){
33179 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33186 inputEl: function ()
33188 return this.el.select('.roo-date-split-field-group-value', true).first();
33191 onRender : function(ct, position)
33195 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33197 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33199 this.dayField = new Roo.bootstrap.ComboBox({
33200 allowBlank : this.dayAllowBlank,
33201 alwaysQuery : true,
33202 displayField : 'value',
33205 forceSelection : true,
33207 placeholder : this.dayPlaceholder,
33208 selectOnFocus : true,
33209 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33210 triggerAction : 'all',
33212 valueField : 'value',
33213 store : new Roo.data.SimpleStore({
33214 data : (function() {
33216 _this.fireEvent('days', _this, days);
33219 fields : [ 'value' ]
33222 select : function (_self, record, index)
33224 _this.setValue(_this.getValue());
33229 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33231 this.monthField = new Roo.bootstrap.MonthField({
33232 after : '<i class=\"fa fa-calendar\"></i>',
33233 allowBlank : this.monthAllowBlank,
33234 placeholder : this.monthPlaceholder,
33237 render : function (_self)
33239 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33240 e.preventDefault();
33244 select : function (_self, oldvalue, newvalue)
33246 _this.setValue(_this.getValue());
33251 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33253 this.yearField = new Roo.bootstrap.ComboBox({
33254 allowBlank : this.yearAllowBlank,
33255 alwaysQuery : true,
33256 displayField : 'value',
33259 forceSelection : true,
33261 placeholder : this.yearPlaceholder,
33262 selectOnFocus : true,
33263 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33264 triggerAction : 'all',
33266 valueField : 'value',
33267 store : new Roo.data.SimpleStore({
33268 data : (function() {
33270 _this.fireEvent('years', _this, years);
33273 fields : [ 'value' ]
33276 select : function (_self, record, index)
33278 _this.setValue(_this.getValue());
33283 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33286 setValue : function(v, format)
33288 this.inputEl.dom.value = v;
33290 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33292 var d = Date.parseDate(v, f);
33299 this.setDay(d.format(this.dayFormat));
33300 this.setMonth(d.format(this.monthFormat));
33301 this.setYear(d.format(this.yearFormat));
33308 setDay : function(v)
33310 this.dayField.setValue(v);
33311 this.inputEl.dom.value = this.getValue();
33316 setMonth : function(v)
33318 this.monthField.setValue(v, true);
33319 this.inputEl.dom.value = this.getValue();
33324 setYear : function(v)
33326 this.yearField.setValue(v);
33327 this.inputEl.dom.value = this.getValue();
33332 getDay : function()
33334 return this.dayField.getValue();
33337 getMonth : function()
33339 return this.monthField.getValue();
33342 getYear : function()
33344 return this.yearField.getValue();
33347 getValue : function()
33349 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33351 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33361 this.inputEl.dom.value = '';
33366 validate : function()
33368 var d = this.dayField.validate();
33369 var m = this.monthField.validate();
33370 var y = this.yearField.validate();
33375 (!this.dayAllowBlank && !d) ||
33376 (!this.monthAllowBlank && !m) ||
33377 (!this.yearAllowBlank && !y)
33382 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33391 this.markInvalid();
33396 markValid : function()
33399 var label = this.el.select('label', true).first();
33400 var icon = this.el.select('i.fa-star', true).first();
33406 this.fireEvent('valid', this);
33410 * Mark this field as invalid
33411 * @param {String} msg The validation message
33413 markInvalid : function(msg)
33416 var label = this.el.select('label', true).first();
33417 var icon = this.el.select('i.fa-star', true).first();
33419 if(label && !icon){
33420 this.el.select('.roo-date-split-field-label', true).createChild({
33422 cls : 'text-danger fa fa-lg fa-star',
33423 tooltip : 'This field is required',
33424 style : 'margin-right:5px;'
33428 this.fireEvent('invalid', this, msg);
33431 clearInvalid : function()
33433 var label = this.el.select('label', true).first();
33434 var icon = this.el.select('i.fa-star', true).first();
33440 this.fireEvent('valid', this);
33443 getName: function()
33453 * http://masonry.desandro.com
33455 * The idea is to render all the bricks based on vertical width...
33457 * The original code extends 'outlayer' - we might need to use that....
33463 * @class Roo.bootstrap.LayoutMasonry
33464 * @extends Roo.bootstrap.Component
33465 * Bootstrap Layout Masonry class
33468 * Create a new Element
33469 * @param {Object} config The config object
33472 Roo.bootstrap.LayoutMasonry = function(config){
33474 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33478 Roo.bootstrap.LayoutMasonry.register(this);
33484 * Fire after layout the items
33485 * @param {Roo.bootstrap.LayoutMasonry} this
33486 * @param {Roo.EventObject} e
33493 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33496 * @cfg {Boolean} isLayoutInstant = no animation?
33498 isLayoutInstant : false, // needed?
33501 * @cfg {Number} boxWidth width of the columns
33506 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33511 * @cfg {Number} padWidth padding below box..
33516 * @cfg {Number} gutter gutter width..
33521 * @cfg {Number} maxCols maximum number of columns
33527 * @cfg {Boolean} isAutoInitial defalut true
33529 isAutoInitial : true,
33534 * @cfg {Boolean} isHorizontal defalut false
33536 isHorizontal : false,
33538 currentSize : null,
33544 bricks: null, //CompositeElement
33548 _isLayoutInited : false,
33550 // isAlternative : false, // only use for vertical layout...
33553 * @cfg {Number} alternativePadWidth padding below box..
33555 alternativePadWidth : 50,
33557 selectedBrick : [],
33559 getAutoCreate : function(){
33561 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33565 cls: 'blog-masonary-wrapper ' + this.cls,
33567 cls : 'mas-boxes masonary'
33574 getChildContainer: function( )
33576 if (this.boxesEl) {
33577 return this.boxesEl;
33580 this.boxesEl = this.el.select('.mas-boxes').first();
33582 return this.boxesEl;
33586 initEvents : function()
33590 if(this.isAutoInitial){
33591 Roo.log('hook children rendered');
33592 this.on('childrenrendered', function() {
33593 Roo.log('children rendered');
33599 initial : function()
33601 this.selectedBrick = [];
33603 this.currentSize = this.el.getBox(true);
33605 Roo.EventManager.onWindowResize(this.resize, this);
33607 if(!this.isAutoInitial){
33615 //this.layout.defer(500,this);
33619 resize : function()
33621 var cs = this.el.getBox(true);
33624 this.currentSize.width == cs.width &&
33625 this.currentSize.x == cs.x &&
33626 this.currentSize.height == cs.height &&
33627 this.currentSize.y == cs.y
33629 Roo.log("no change in with or X or Y");
33633 this.currentSize = cs;
33639 layout : function()
33641 this._resetLayout();
33643 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33645 this.layoutItems( isInstant );
33647 this._isLayoutInited = true;
33649 this.fireEvent('layout', this);
33653 _resetLayout : function()
33655 if(this.isHorizontal){
33656 this.horizontalMeasureColumns();
33660 this.verticalMeasureColumns();
33664 verticalMeasureColumns : function()
33666 this.getContainerWidth();
33668 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33669 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33673 var boxWidth = this.boxWidth + this.padWidth;
33675 if(this.containerWidth < this.boxWidth){
33676 boxWidth = this.containerWidth
33679 var containerWidth = this.containerWidth;
33681 var cols = Math.floor(containerWidth / boxWidth);
33683 this.cols = Math.max( cols, 1 );
33685 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33687 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33689 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33691 this.colWidth = boxWidth + avail - this.padWidth;
33693 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33694 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33697 horizontalMeasureColumns : function()
33699 this.getContainerWidth();
33701 var boxWidth = this.boxWidth;
33703 if(this.containerWidth < boxWidth){
33704 boxWidth = this.containerWidth;
33707 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33709 this.el.setHeight(boxWidth);
33713 getContainerWidth : function()
33715 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33718 layoutItems : function( isInstant )
33720 Roo.log(this.bricks);
33722 var items = Roo.apply([], this.bricks);
33724 if(this.isHorizontal){
33725 this._horizontalLayoutItems( items , isInstant );
33729 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33730 // this._verticalAlternativeLayoutItems( items , isInstant );
33734 this._verticalLayoutItems( items , isInstant );
33738 _verticalLayoutItems : function ( items , isInstant)
33740 if ( !items || !items.length ) {
33745 ['xs', 'xs', 'xs', 'tall'],
33746 ['xs', 'xs', 'tall'],
33747 ['xs', 'xs', 'sm'],
33748 ['xs', 'xs', 'xs'],
33754 ['sm', 'xs', 'xs'],
33758 ['tall', 'xs', 'xs', 'xs'],
33759 ['tall', 'xs', 'xs'],
33771 Roo.each(items, function(item, k){
33773 switch (item.size) {
33774 // these layouts take up a full box,
33785 boxes.push([item]);
33808 var filterPattern = function(box, length)
33816 var pattern = box.slice(0, length);
33820 Roo.each(pattern, function(i){
33821 format.push(i.size);
33824 Roo.each(standard, function(s){
33826 if(String(s) != String(format)){
33835 if(!match && length == 1){
33840 filterPattern(box, length - 1);
33844 queue.push(pattern);
33846 box = box.slice(length, box.length);
33848 filterPattern(box, 4);
33854 Roo.each(boxes, function(box, k){
33860 if(box.length == 1){
33865 filterPattern(box, 4);
33869 this._processVerticalLayoutQueue( queue, isInstant );
33873 // _verticalAlternativeLayoutItems : function( items , isInstant )
33875 // if ( !items || !items.length ) {
33879 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33883 _horizontalLayoutItems : function ( items , isInstant)
33885 if ( !items || !items.length || items.length < 3) {
33891 var eItems = items.slice(0, 3);
33893 items = items.slice(3, items.length);
33896 ['xs', 'xs', 'xs', 'wide'],
33897 ['xs', 'xs', 'wide'],
33898 ['xs', 'xs', 'sm'],
33899 ['xs', 'xs', 'xs'],
33905 ['sm', 'xs', 'xs'],
33909 ['wide', 'xs', 'xs', 'xs'],
33910 ['wide', 'xs', 'xs'],
33923 Roo.each(items, function(item, k){
33925 switch (item.size) {
33936 boxes.push([item]);
33960 var filterPattern = function(box, length)
33968 var pattern = box.slice(0, length);
33972 Roo.each(pattern, function(i){
33973 format.push(i.size);
33976 Roo.each(standard, function(s){
33978 if(String(s) != String(format)){
33987 if(!match && length == 1){
33992 filterPattern(box, length - 1);
33996 queue.push(pattern);
33998 box = box.slice(length, box.length);
34000 filterPattern(box, 4);
34006 Roo.each(boxes, function(box, k){
34012 if(box.length == 1){
34017 filterPattern(box, 4);
34024 var pos = this.el.getBox(true);
34028 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34030 var hit_end = false;
34032 Roo.each(queue, function(box){
34036 Roo.each(box, function(b){
34038 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34048 Roo.each(box, function(b){
34050 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34053 mx = Math.max(mx, b.x);
34057 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34061 Roo.each(box, function(b){
34063 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34077 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34080 /** Sets position of item in DOM
34081 * @param {Element} item
34082 * @param {Number} x - horizontal position
34083 * @param {Number} y - vertical position
34084 * @param {Boolean} isInstant - disables transitions
34086 _processVerticalLayoutQueue : function( queue, isInstant )
34088 var pos = this.el.getBox(true);
34093 for (var i = 0; i < this.cols; i++){
34097 Roo.each(queue, function(box, k){
34099 var col = k % this.cols;
34101 Roo.each(box, function(b,kk){
34103 b.el.position('absolute');
34105 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34106 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34108 if(b.size == 'md-left' || b.size == 'md-right'){
34109 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34110 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34113 b.el.setWidth(width);
34114 b.el.setHeight(height);
34116 b.el.select('iframe',true).setSize(width,height);
34120 for (var i = 0; i < this.cols; i++){
34122 if(maxY[i] < maxY[col]){
34127 col = Math.min(col, i);
34131 x = pos.x + col * (this.colWidth + this.padWidth);
34135 var positions = [];
34137 switch (box.length){
34139 positions = this.getVerticalOneBoxColPositions(x, y, box);
34142 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34145 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34148 positions = this.getVerticalFourBoxColPositions(x, y, box);
34154 Roo.each(box, function(b,kk){
34156 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34158 var sz = b.el.getSize();
34160 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34168 for (var i = 0; i < this.cols; i++){
34169 mY = Math.max(mY, maxY[i]);
34172 this.el.setHeight(mY - pos.y);
34176 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34178 // var pos = this.el.getBox(true);
34181 // var maxX = pos.right;
34183 // var maxHeight = 0;
34185 // Roo.each(items, function(item, k){
34189 // item.el.position('absolute');
34191 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34193 // item.el.setWidth(width);
34195 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34197 // item.el.setHeight(height);
34200 // item.el.setXY([x, y], isInstant ? false : true);
34202 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34205 // y = y + height + this.alternativePadWidth;
34207 // maxHeight = maxHeight + height + this.alternativePadWidth;
34211 // this.el.setHeight(maxHeight);
34215 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34217 var pos = this.el.getBox(true);
34222 var maxX = pos.right;
34224 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34226 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34228 Roo.each(queue, function(box, k){
34230 Roo.each(box, function(b, kk){
34232 b.el.position('absolute');
34234 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34235 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34237 if(b.size == 'md-left' || b.size == 'md-right'){
34238 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34239 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34242 b.el.setWidth(width);
34243 b.el.setHeight(height);
34251 var positions = [];
34253 switch (box.length){
34255 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34258 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34261 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34264 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34270 Roo.each(box, function(b,kk){
34272 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34274 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34282 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34284 Roo.each(eItems, function(b,k){
34286 b.size = (k == 0) ? 'sm' : 'xs';
34287 b.x = (k == 0) ? 2 : 1;
34288 b.y = (k == 0) ? 2 : 1;
34290 b.el.position('absolute');
34292 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34294 b.el.setWidth(width);
34296 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34298 b.el.setHeight(height);
34302 var positions = [];
34305 x : maxX - this.unitWidth * 2 - this.gutter,
34310 x : maxX - this.unitWidth,
34311 y : minY + (this.unitWidth + this.gutter) * 2
34315 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34319 Roo.each(eItems, function(b,k){
34321 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34327 getVerticalOneBoxColPositions : function(x, y, box)
34331 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34333 if(box[0].size == 'md-left'){
34337 if(box[0].size == 'md-right'){
34342 x : x + (this.unitWidth + this.gutter) * rand,
34349 getVerticalTwoBoxColPositions : function(x, y, box)
34353 if(box[0].size == 'xs'){
34357 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34361 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34375 x : x + (this.unitWidth + this.gutter) * 2,
34376 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34383 getVerticalThreeBoxColPositions : function(x, y, box)
34387 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34395 x : x + (this.unitWidth + this.gutter) * 1,
34400 x : x + (this.unitWidth + this.gutter) * 2,
34408 if(box[0].size == 'xs' && box[1].size == 'xs'){
34417 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34421 x : x + (this.unitWidth + this.gutter) * 1,
34435 x : x + (this.unitWidth + this.gutter) * 2,
34440 x : x + (this.unitWidth + this.gutter) * 2,
34441 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34448 getVerticalFourBoxColPositions : function(x, y, box)
34452 if(box[0].size == 'xs'){
34461 y : y + (this.unitHeight + this.gutter) * 1
34466 y : y + (this.unitHeight + this.gutter) * 2
34470 x : x + (this.unitWidth + this.gutter) * 1,
34484 x : x + (this.unitWidth + this.gutter) * 2,
34489 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34490 y : y + (this.unitHeight + this.gutter) * 1
34494 x : x + (this.unitWidth + this.gutter) * 2,
34495 y : y + (this.unitWidth + this.gutter) * 2
34502 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34506 if(box[0].size == 'md-left'){
34508 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34515 if(box[0].size == 'md-right'){
34517 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34518 y : minY + (this.unitWidth + this.gutter) * 1
34524 var rand = Math.floor(Math.random() * (4 - box[0].y));
34527 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34528 y : minY + (this.unitWidth + this.gutter) * rand
34535 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34539 if(box[0].size == 'xs'){
34542 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34547 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34548 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34556 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34561 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34562 y : minY + (this.unitWidth + this.gutter) * 2
34569 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34573 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34576 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34581 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34582 y : minY + (this.unitWidth + this.gutter) * 1
34586 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34587 y : minY + (this.unitWidth + this.gutter) * 2
34594 if(box[0].size == 'xs' && box[1].size == 'xs'){
34597 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34602 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34607 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34608 y : minY + (this.unitWidth + this.gutter) * 1
34616 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34621 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34622 y : minY + (this.unitWidth + this.gutter) * 2
34626 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34627 y : minY + (this.unitWidth + this.gutter) * 2
34634 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34638 if(box[0].size == 'xs'){
34641 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34646 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34651 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),
34656 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34657 y : minY + (this.unitWidth + this.gutter) * 1
34665 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34670 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34671 y : minY + (this.unitWidth + this.gutter) * 2
34675 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34676 y : minY + (this.unitWidth + this.gutter) * 2
34680 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),
34681 y : minY + (this.unitWidth + this.gutter) * 2
34689 * remove a Masonry Brick
34690 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34692 removeBrick : function(brick_id)
34698 for (var i = 0; i<this.bricks.length; i++) {
34699 if (this.bricks[i].id == brick_id) {
34700 this.bricks.splice(i,1);
34701 this.el.dom.removeChild(Roo.get(brick_id).dom);
34708 * adds a Masonry Brick
34709 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34711 addBrick : function(cfg)
34713 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34714 //this.register(cn);
34715 cn.parentId = this.id;
34716 cn.render(this.el);
34721 * register a Masonry Brick
34722 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34725 register : function(brick)
34727 this.bricks.push(brick);
34728 brick.masonryId = this.id;
34732 * clear all the Masonry Brick
34734 clearAll : function()
34737 //this.getChildContainer().dom.innerHTML = "";
34738 this.el.dom.innerHTML = '';
34741 getSelected : function()
34743 if (!this.selectedBrick) {
34747 return this.selectedBrick;
34751 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34755 * register a Masonry Layout
34756 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34759 register : function(layout)
34761 this.groups[layout.id] = layout;
34764 * fetch a Masonry Layout based on the masonry layout ID
34765 * @param {string} the masonry layout to add
34766 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34769 get: function(layout_id) {
34770 if (typeof(this.groups[layout_id]) == 'undefined') {
34773 return this.groups[layout_id] ;
34785 * http://masonry.desandro.com
34787 * The idea is to render all the bricks based on vertical width...
34789 * The original code extends 'outlayer' - we might need to use that....
34795 * @class Roo.bootstrap.LayoutMasonryAuto
34796 * @extends Roo.bootstrap.Component
34797 * Bootstrap Layout Masonry class
34800 * Create a new Element
34801 * @param {Object} config The config object
34804 Roo.bootstrap.LayoutMasonryAuto = function(config){
34805 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34808 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34811 * @cfg {Boolean} isFitWidth - resize the width..
34813 isFitWidth : false, // options..
34815 * @cfg {Boolean} isOriginLeft = left align?
34817 isOriginLeft : true,
34819 * @cfg {Boolean} isOriginTop = top align?
34821 isOriginTop : false,
34823 * @cfg {Boolean} isLayoutInstant = no animation?
34825 isLayoutInstant : false, // needed?
34827 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34829 isResizingContainer : true,
34831 * @cfg {Number} columnWidth width of the columns
34837 * @cfg {Number} maxCols maximum number of columns
34842 * @cfg {Number} padHeight padding below box..
34848 * @cfg {Boolean} isAutoInitial defalut true
34851 isAutoInitial : true,
34857 initialColumnWidth : 0,
34858 currentSize : null,
34860 colYs : null, // array.
34867 bricks: null, //CompositeElement
34868 cols : 0, // array?
34869 // element : null, // wrapped now this.el
34870 _isLayoutInited : null,
34873 getAutoCreate : function(){
34877 cls: 'blog-masonary-wrapper ' + this.cls,
34879 cls : 'mas-boxes masonary'
34886 getChildContainer: function( )
34888 if (this.boxesEl) {
34889 return this.boxesEl;
34892 this.boxesEl = this.el.select('.mas-boxes').first();
34894 return this.boxesEl;
34898 initEvents : function()
34902 if(this.isAutoInitial){
34903 Roo.log('hook children rendered');
34904 this.on('childrenrendered', function() {
34905 Roo.log('children rendered');
34912 initial : function()
34914 this.reloadItems();
34916 this.currentSize = this.el.getBox(true);
34918 /// was window resize... - let's see if this works..
34919 Roo.EventManager.onWindowResize(this.resize, this);
34921 if(!this.isAutoInitial){
34926 this.layout.defer(500,this);
34929 reloadItems: function()
34931 this.bricks = this.el.select('.masonry-brick', true);
34933 this.bricks.each(function(b) {
34934 //Roo.log(b.getSize());
34935 if (!b.attr('originalwidth')) {
34936 b.attr('originalwidth', b.getSize().width);
34941 Roo.log(this.bricks.elements.length);
34944 resize : function()
34947 var cs = this.el.getBox(true);
34949 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34950 Roo.log("no change in with or X");
34953 this.currentSize = cs;
34957 layout : function()
34960 this._resetLayout();
34961 //this._manageStamps();
34963 // don't animate first layout
34964 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34965 this.layoutItems( isInstant );
34967 // flag for initalized
34968 this._isLayoutInited = true;
34971 layoutItems : function( isInstant )
34973 //var items = this._getItemsForLayout( this.items );
34974 // original code supports filtering layout items.. we just ignore it..
34976 this._layoutItems( this.bricks , isInstant );
34978 this._postLayout();
34980 _layoutItems : function ( items , isInstant)
34982 //this.fireEvent( 'layout', this, items );
34985 if ( !items || !items.elements.length ) {
34986 // no items, emit event with empty array
34991 items.each(function(item) {
34992 Roo.log("layout item");
34994 // get x/y object from method
34995 var position = this._getItemLayoutPosition( item );
34997 position.item = item;
34998 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34999 queue.push( position );
35002 this._processLayoutQueue( queue );
35004 /** Sets position of item in DOM
35005 * @param {Element} item
35006 * @param {Number} x - horizontal position
35007 * @param {Number} y - vertical position
35008 * @param {Boolean} isInstant - disables transitions
35010 _processLayoutQueue : function( queue )
35012 for ( var i=0, len = queue.length; i < len; i++ ) {
35013 var obj = queue[i];
35014 obj.item.position('absolute');
35015 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35021 * Any logic you want to do after each layout,
35022 * i.e. size the container
35024 _postLayout : function()
35026 this.resizeContainer();
35029 resizeContainer : function()
35031 if ( !this.isResizingContainer ) {
35034 var size = this._getContainerSize();
35036 this.el.setSize(size.width,size.height);
35037 this.boxesEl.setSize(size.width,size.height);
35043 _resetLayout : function()
35045 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35046 this.colWidth = this.el.getWidth();
35047 //this.gutter = this.el.getWidth();
35049 this.measureColumns();
35055 this.colYs.push( 0 );
35061 measureColumns : function()
35063 this.getContainerWidth();
35064 // if columnWidth is 0, default to outerWidth of first item
35065 if ( !this.columnWidth ) {
35066 var firstItem = this.bricks.first();
35067 Roo.log(firstItem);
35068 this.columnWidth = this.containerWidth;
35069 if (firstItem && firstItem.attr('originalwidth') ) {
35070 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35072 // columnWidth fall back to item of first element
35073 Roo.log("set column width?");
35074 this.initialColumnWidth = this.columnWidth ;
35076 // if first elem has no width, default to size of container
35081 if (this.initialColumnWidth) {
35082 this.columnWidth = this.initialColumnWidth;
35087 // column width is fixed at the top - however if container width get's smaller we should
35090 // this bit calcs how man columns..
35092 var columnWidth = this.columnWidth += this.gutter;
35094 // calculate columns
35095 var containerWidth = this.containerWidth + this.gutter;
35097 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35098 // fix rounding errors, typically with gutters
35099 var excess = columnWidth - containerWidth % columnWidth;
35102 // if overshoot is less than a pixel, round up, otherwise floor it
35103 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35104 cols = Math[ mathMethod ]( cols );
35105 this.cols = Math.max( cols, 1 );
35106 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35108 // padding positioning..
35109 var totalColWidth = this.cols * this.columnWidth;
35110 var padavail = this.containerWidth - totalColWidth;
35111 // so for 2 columns - we need 3 'pads'
35113 var padNeeded = (1+this.cols) * this.padWidth;
35115 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35117 this.columnWidth += padExtra
35118 //this.padWidth = Math.floor(padavail / ( this.cols));
35120 // adjust colum width so that padding is fixed??
35122 // we have 3 columns ... total = width * 3
35123 // we have X left over... that should be used by
35125 //if (this.expandC) {
35133 getContainerWidth : function()
35135 /* // container is parent if fit width
35136 var container = this.isFitWidth ? this.element.parentNode : this.element;
35137 // check that this.size and size are there
35138 // IE8 triggers resize on body size change, so they might not be
35140 var size = getSize( container ); //FIXME
35141 this.containerWidth = size && size.innerWidth; //FIXME
35144 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35148 _getItemLayoutPosition : function( item ) // what is item?
35150 // we resize the item to our columnWidth..
35152 item.setWidth(this.columnWidth);
35153 item.autoBoxAdjust = false;
35155 var sz = item.getSize();
35157 // how many columns does this brick span
35158 var remainder = this.containerWidth % this.columnWidth;
35160 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35161 // round if off by 1 pixel, otherwise use ceil
35162 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35163 colSpan = Math.min( colSpan, this.cols );
35165 // normally this should be '1' as we dont' currently allow multi width columns..
35167 var colGroup = this._getColGroup( colSpan );
35168 // get the minimum Y value from the columns
35169 var minimumY = Math.min.apply( Math, colGroup );
35170 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35172 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35174 // position the brick
35176 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35177 y: this.currentSize.y + minimumY + this.padHeight
35181 // apply setHeight to necessary columns
35182 var setHeight = minimumY + sz.height + this.padHeight;
35183 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35185 var setSpan = this.cols + 1 - colGroup.length;
35186 for ( var i = 0; i < setSpan; i++ ) {
35187 this.colYs[ shortColIndex + i ] = setHeight ;
35194 * @param {Number} colSpan - number of columns the element spans
35195 * @returns {Array} colGroup
35197 _getColGroup : function( colSpan )
35199 if ( colSpan < 2 ) {
35200 // if brick spans only one column, use all the column Ys
35205 // how many different places could this brick fit horizontally
35206 var groupCount = this.cols + 1 - colSpan;
35207 // for each group potential horizontal position
35208 for ( var i = 0; i < groupCount; i++ ) {
35209 // make an array of colY values for that one group
35210 var groupColYs = this.colYs.slice( i, i + colSpan );
35211 // and get the max value of the array
35212 colGroup[i] = Math.max.apply( Math, groupColYs );
35217 _manageStamp : function( stamp )
35219 var stampSize = stamp.getSize();
35220 var offset = stamp.getBox();
35221 // get the columns that this stamp affects
35222 var firstX = this.isOriginLeft ? offset.x : offset.right;
35223 var lastX = firstX + stampSize.width;
35224 var firstCol = Math.floor( firstX / this.columnWidth );
35225 firstCol = Math.max( 0, firstCol );
35227 var lastCol = Math.floor( lastX / this.columnWidth );
35228 // lastCol should not go over if multiple of columnWidth #425
35229 lastCol -= lastX % this.columnWidth ? 0 : 1;
35230 lastCol = Math.min( this.cols - 1, lastCol );
35232 // set colYs to bottom of the stamp
35233 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35236 for ( var i = firstCol; i <= lastCol; i++ ) {
35237 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35242 _getContainerSize : function()
35244 this.maxY = Math.max.apply( Math, this.colYs );
35249 if ( this.isFitWidth ) {
35250 size.width = this._getContainerFitWidth();
35256 _getContainerFitWidth : function()
35258 var unusedCols = 0;
35259 // count unused columns
35262 if ( this.colYs[i] !== 0 ) {
35267 // fit container to columns that have been used
35268 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35271 needsResizeLayout : function()
35273 var previousWidth = this.containerWidth;
35274 this.getContainerWidth();
35275 return previousWidth !== this.containerWidth;
35290 * @class Roo.bootstrap.MasonryBrick
35291 * @extends Roo.bootstrap.Component
35292 * Bootstrap MasonryBrick class
35295 * Create a new MasonryBrick
35296 * @param {Object} config The config object
35299 Roo.bootstrap.MasonryBrick = function(config){
35301 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35303 Roo.bootstrap.MasonryBrick.register(this);
35309 * When a MasonryBrick is clcik
35310 * @param {Roo.bootstrap.MasonryBrick} this
35311 * @param {Roo.EventObject} e
35317 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35320 * @cfg {String} title
35324 * @cfg {String} html
35328 * @cfg {String} bgimage
35332 * @cfg {String} videourl
35336 * @cfg {String} cls
35340 * @cfg {String} href
35344 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35349 * @cfg {String} placetitle (center|bottom)
35354 * @cfg {Boolean} isFitContainer defalut true
35356 isFitContainer : true,
35359 * @cfg {Boolean} preventDefault defalut false
35361 preventDefault : false,
35364 * @cfg {Boolean} inverse defalut false
35366 maskInverse : false,
35368 getAutoCreate : function()
35370 if(!this.isFitContainer){
35371 return this.getSplitAutoCreate();
35374 var cls = 'masonry-brick masonry-brick-full';
35376 if(this.href.length){
35377 cls += ' masonry-brick-link';
35380 if(this.bgimage.length){
35381 cls += ' masonry-brick-image';
35384 if(this.maskInverse){
35385 cls += ' mask-inverse';
35388 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35389 cls += ' enable-mask';
35393 cls += ' masonry-' + this.size + '-brick';
35396 if(this.placetitle.length){
35398 switch (this.placetitle) {
35400 cls += ' masonry-center-title';
35403 cls += ' masonry-bottom-title';
35410 if(!this.html.length && !this.bgimage.length){
35411 cls += ' masonry-center-title';
35414 if(!this.html.length && this.bgimage.length){
35415 cls += ' masonry-bottom-title';
35420 cls += ' ' + this.cls;
35424 tag: (this.href.length) ? 'a' : 'div',
35429 cls: 'masonry-brick-mask'
35433 cls: 'masonry-brick-paragraph',
35439 if(this.href.length){
35440 cfg.href = this.href;
35443 var cn = cfg.cn[1].cn;
35445 if(this.title.length){
35448 cls: 'masonry-brick-title',
35453 if(this.html.length){
35456 cls: 'masonry-brick-text',
35461 if (!this.title.length && !this.html.length) {
35462 cfg.cn[1].cls += ' hide';
35465 if(this.bgimage.length){
35468 cls: 'masonry-brick-image-view',
35473 if(this.videourl.length){
35474 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35475 // youtube support only?
35478 cls: 'masonry-brick-image-view',
35481 allowfullscreen : true
35489 getSplitAutoCreate : function()
35491 var cls = 'masonry-brick masonry-brick-split';
35493 if(this.href.length){
35494 cls += ' masonry-brick-link';
35497 if(this.bgimage.length){
35498 cls += ' masonry-brick-image';
35502 cls += ' masonry-' + this.size + '-brick';
35505 switch (this.placetitle) {
35507 cls += ' masonry-center-title';
35510 cls += ' masonry-bottom-title';
35513 if(!this.bgimage.length){
35514 cls += ' masonry-center-title';
35517 if(this.bgimage.length){
35518 cls += ' masonry-bottom-title';
35524 cls += ' ' + this.cls;
35528 tag: (this.href.length) ? 'a' : 'div',
35533 cls: 'masonry-brick-split-head',
35537 cls: 'masonry-brick-paragraph',
35544 cls: 'masonry-brick-split-body',
35550 if(this.href.length){
35551 cfg.href = this.href;
35554 if(this.title.length){
35555 cfg.cn[0].cn[0].cn.push({
35557 cls: 'masonry-brick-title',
35562 if(this.html.length){
35563 cfg.cn[1].cn.push({
35565 cls: 'masonry-brick-text',
35570 if(this.bgimage.length){
35571 cfg.cn[0].cn.push({
35573 cls: 'masonry-brick-image-view',
35578 if(this.videourl.length){
35579 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35580 // youtube support only?
35581 cfg.cn[0].cn.cn.push({
35583 cls: 'masonry-brick-image-view',
35586 allowfullscreen : true
35593 initEvents: function()
35595 switch (this.size) {
35628 this.el.on('touchstart', this.onTouchStart, this);
35629 this.el.on('touchmove', this.onTouchMove, this);
35630 this.el.on('touchend', this.onTouchEnd, this);
35631 this.el.on('contextmenu', this.onContextMenu, this);
35633 this.el.on('mouseenter' ,this.enter, this);
35634 this.el.on('mouseleave', this.leave, this);
35635 this.el.on('click', this.onClick, this);
35638 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35639 this.parent().bricks.push(this);
35644 onClick: function(e, el)
35646 var time = this.endTimer - this.startTimer;
35647 // Roo.log(e.preventDefault());
35650 e.preventDefault();
35655 if(!this.preventDefault){
35659 e.preventDefault();
35661 if (this.activeClass != '') {
35662 this.selectBrick();
35665 this.fireEvent('click', this, e);
35668 enter: function(e, el)
35670 e.preventDefault();
35672 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35676 if(this.bgimage.length && this.html.length){
35677 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35681 leave: function(e, el)
35683 e.preventDefault();
35685 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35689 if(this.bgimage.length && this.html.length){
35690 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35694 onTouchStart: function(e, el)
35696 // e.preventDefault();
35698 this.touchmoved = false;
35700 if(!this.isFitContainer){
35704 if(!this.bgimage.length || !this.html.length){
35708 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35710 this.timer = new Date().getTime();
35714 onTouchMove: function(e, el)
35716 this.touchmoved = true;
35719 onContextMenu : function(e,el)
35721 e.preventDefault();
35722 e.stopPropagation();
35726 onTouchEnd: function(e, el)
35728 // e.preventDefault();
35730 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35737 if(!this.bgimage.length || !this.html.length){
35739 if(this.href.length){
35740 window.location.href = this.href;
35746 if(!this.isFitContainer){
35750 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35752 window.location.href = this.href;
35755 //selection on single brick only
35756 selectBrick : function() {
35758 if (!this.parentId) {
35762 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35763 var index = m.selectedBrick.indexOf(this.id);
35766 m.selectedBrick.splice(index,1);
35767 this.el.removeClass(this.activeClass);
35771 for(var i = 0; i < m.selectedBrick.length; i++) {
35772 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35773 b.el.removeClass(b.activeClass);
35776 m.selectedBrick = [];
35778 m.selectedBrick.push(this.id);
35779 this.el.addClass(this.activeClass);
35783 isSelected : function(){
35784 return this.el.hasClass(this.activeClass);
35789 Roo.apply(Roo.bootstrap.MasonryBrick, {
35792 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35794 * register a Masonry Brick
35795 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35798 register : function(brick)
35800 //this.groups[brick.id] = brick;
35801 this.groups.add(brick.id, brick);
35804 * fetch a masonry brick based on the masonry brick ID
35805 * @param {string} the masonry brick to add
35806 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35809 get: function(brick_id)
35811 // if (typeof(this.groups[brick_id]) == 'undefined') {
35814 // return this.groups[brick_id] ;
35816 if(this.groups.key(brick_id)) {
35817 return this.groups.key(brick_id);
35835 * @class Roo.bootstrap.Brick
35836 * @extends Roo.bootstrap.Component
35837 * Bootstrap Brick class
35840 * Create a new Brick
35841 * @param {Object} config The config object
35844 Roo.bootstrap.Brick = function(config){
35845 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35851 * When a Brick is click
35852 * @param {Roo.bootstrap.Brick} this
35853 * @param {Roo.EventObject} e
35859 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35862 * @cfg {String} title
35866 * @cfg {String} html
35870 * @cfg {String} bgimage
35874 * @cfg {String} cls
35878 * @cfg {String} href
35882 * @cfg {String} video
35886 * @cfg {Boolean} square
35890 getAutoCreate : function()
35892 var cls = 'roo-brick';
35894 if(this.href.length){
35895 cls += ' roo-brick-link';
35898 if(this.bgimage.length){
35899 cls += ' roo-brick-image';
35902 if(!this.html.length && !this.bgimage.length){
35903 cls += ' roo-brick-center-title';
35906 if(!this.html.length && this.bgimage.length){
35907 cls += ' roo-brick-bottom-title';
35911 cls += ' ' + this.cls;
35915 tag: (this.href.length) ? 'a' : 'div',
35920 cls: 'roo-brick-paragraph',
35926 if(this.href.length){
35927 cfg.href = this.href;
35930 var cn = cfg.cn[0].cn;
35932 if(this.title.length){
35935 cls: 'roo-brick-title',
35940 if(this.html.length){
35943 cls: 'roo-brick-text',
35950 if(this.bgimage.length){
35953 cls: 'roo-brick-image-view',
35961 initEvents: function()
35963 if(this.title.length || this.html.length){
35964 this.el.on('mouseenter' ,this.enter, this);
35965 this.el.on('mouseleave', this.leave, this);
35968 Roo.EventManager.onWindowResize(this.resize, this);
35970 if(this.bgimage.length){
35971 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35972 this.imageEl.on('load', this.onImageLoad, this);
35979 onImageLoad : function()
35984 resize : function()
35986 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35988 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35990 if(this.bgimage.length){
35991 var image = this.el.select('.roo-brick-image-view', true).first();
35993 image.setWidth(paragraph.getWidth());
35996 image.setHeight(paragraph.getWidth());
35999 this.el.setHeight(image.getHeight());
36000 paragraph.setHeight(image.getHeight());
36006 enter: function(e, el)
36008 e.preventDefault();
36010 if(this.bgimage.length){
36011 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36012 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36016 leave: function(e, el)
36018 e.preventDefault();
36020 if(this.bgimage.length){
36021 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36022 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36037 * @class Roo.bootstrap.NumberField
36038 * @extends Roo.bootstrap.Input
36039 * Bootstrap NumberField class
36045 * Create a new NumberField
36046 * @param {Object} config The config object
36049 Roo.bootstrap.NumberField = function(config){
36050 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36053 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36056 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36058 allowDecimals : true,
36060 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36062 decimalSeparator : ".",
36064 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36066 decimalPrecision : 2,
36068 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36070 allowNegative : true,
36073 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36077 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36079 minValue : Number.NEGATIVE_INFINITY,
36081 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36083 maxValue : Number.MAX_VALUE,
36085 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36087 minText : "The minimum value for this field is {0}",
36089 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36091 maxText : "The maximum value for this field is {0}",
36093 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36094 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36096 nanText : "{0} is not a valid number",
36098 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36100 thousandsDelimiter : false,
36102 * @cfg {String} valueAlign alignment of value
36104 valueAlign : "left",
36106 getAutoCreate : function()
36108 var hiddenInput = {
36112 cls: 'hidden-number-input'
36116 hiddenInput.name = this.name;
36121 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36123 this.name = hiddenInput.name;
36125 if(cfg.cn.length > 0) {
36126 cfg.cn.push(hiddenInput);
36133 initEvents : function()
36135 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36137 var allowed = "0123456789";
36139 if(this.allowDecimals){
36140 allowed += this.decimalSeparator;
36143 if(this.allowNegative){
36147 if(this.thousandsDelimiter) {
36151 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36153 var keyPress = function(e){
36155 var k = e.getKey();
36157 var c = e.getCharCode();
36160 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36161 allowed.indexOf(String.fromCharCode(c)) === -1
36167 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36171 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36176 this.el.on("keypress", keyPress, this);
36179 validateValue : function(value)
36182 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36186 var num = this.parseValue(value);
36189 this.markInvalid(String.format(this.nanText, value));
36193 if(num < this.minValue){
36194 this.markInvalid(String.format(this.minText, this.minValue));
36198 if(num > this.maxValue){
36199 this.markInvalid(String.format(this.maxText, this.maxValue));
36206 getValue : function()
36208 var v = this.hiddenEl().getValue();
36210 return this.fixPrecision(this.parseValue(v));
36213 parseValue : function(value)
36215 if(this.thousandsDelimiter) {
36217 r = new RegExp(",", "g");
36218 value = value.replace(r, "");
36221 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36222 return isNaN(value) ? '' : value;
36225 fixPrecision : function(value)
36227 if(this.thousandsDelimiter) {
36229 r = new RegExp(",", "g");
36230 value = value.replace(r, "");
36233 var nan = isNaN(value);
36235 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36236 return nan ? '' : value;
36238 return parseFloat(value).toFixed(this.decimalPrecision);
36241 setValue : function(v)
36243 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36249 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36251 this.inputEl().dom.value = (v == '') ? '' :
36252 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36254 if(!this.allowZero && v === '0') {
36255 this.hiddenEl().dom.value = '';
36256 this.inputEl().dom.value = '';
36263 decimalPrecisionFcn : function(v)
36265 return Math.floor(v);
36268 beforeBlur : function()
36270 var v = this.parseValue(this.getRawValue());
36272 if(v || v === 0 || v === ''){
36277 hiddenEl : function()
36279 return this.el.select('input.hidden-number-input',true).first();
36291 * @class Roo.bootstrap.DocumentSlider
36292 * @extends Roo.bootstrap.Component
36293 * Bootstrap DocumentSlider class
36296 * Create a new DocumentViewer
36297 * @param {Object} config The config object
36300 Roo.bootstrap.DocumentSlider = function(config){
36301 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36308 * Fire after initEvent
36309 * @param {Roo.bootstrap.DocumentSlider} this
36314 * Fire after update
36315 * @param {Roo.bootstrap.DocumentSlider} this
36321 * @param {Roo.bootstrap.DocumentSlider} this
36327 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36333 getAutoCreate : function()
36337 cls : 'roo-document-slider',
36341 cls : 'roo-document-slider-header',
36345 cls : 'roo-document-slider-header-title'
36351 cls : 'roo-document-slider-body',
36355 cls : 'roo-document-slider-prev',
36359 cls : 'fa fa-chevron-left'
36365 cls : 'roo-document-slider-thumb',
36369 cls : 'roo-document-slider-image'
36375 cls : 'roo-document-slider-next',
36379 cls : 'fa fa-chevron-right'
36391 initEvents : function()
36393 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36394 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36396 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36397 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36399 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36400 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36402 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36403 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36405 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36406 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36408 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36409 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36411 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36412 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36414 this.thumbEl.on('click', this.onClick, this);
36416 this.prevIndicator.on('click', this.prev, this);
36418 this.nextIndicator.on('click', this.next, this);
36422 initial : function()
36424 if(this.files.length){
36425 this.indicator = 1;
36429 this.fireEvent('initial', this);
36432 update : function()
36434 this.imageEl.attr('src', this.files[this.indicator - 1]);
36436 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36438 this.prevIndicator.show();
36440 if(this.indicator == 1){
36441 this.prevIndicator.hide();
36444 this.nextIndicator.show();
36446 if(this.indicator == this.files.length){
36447 this.nextIndicator.hide();
36450 this.thumbEl.scrollTo('top');
36452 this.fireEvent('update', this);
36455 onClick : function(e)
36457 e.preventDefault();
36459 this.fireEvent('click', this);
36464 e.preventDefault();
36466 this.indicator = Math.max(1, this.indicator - 1);
36473 e.preventDefault();
36475 this.indicator = Math.min(this.files.length, this.indicator + 1);
36489 * @class Roo.bootstrap.RadioSet
36490 * @extends Roo.bootstrap.Input
36491 * Bootstrap RadioSet class
36492 * @cfg {String} indicatorpos (left|right) default left
36493 * @cfg {Boolean} inline (true|false) inline the element (default true)
36494 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36496 * Create a new RadioSet
36497 * @param {Object} config The config object
36500 Roo.bootstrap.RadioSet = function(config){
36502 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36506 Roo.bootstrap.RadioSet.register(this);
36511 * Fires when the element is checked or unchecked.
36512 * @param {Roo.bootstrap.RadioSet} this This radio
36513 * @param {Roo.bootstrap.Radio} item The checked item
36518 * Fires when the element is click.
36519 * @param {Roo.bootstrap.RadioSet} this This radio set
36520 * @param {Roo.bootstrap.Radio} item The checked item
36521 * @param {Roo.EventObject} e The event object
36528 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36536 indicatorpos : 'left',
36538 getAutoCreate : function()
36542 cls : 'roo-radio-set-label',
36546 html : this.fieldLabel
36550 if (Roo.bootstrap.version == 3) {
36553 if(this.indicatorpos == 'left'){
36556 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36557 tooltip : 'This field is required'
36562 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36563 tooltip : 'This field is required'
36569 cls : 'roo-radio-set-items'
36572 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36574 if (align === 'left' && this.fieldLabel.length) {
36577 cls : "roo-radio-set-right",
36583 if(this.labelWidth > 12){
36584 label.style = "width: " + this.labelWidth + 'px';
36587 if(this.labelWidth < 13 && this.labelmd == 0){
36588 this.labelmd = this.labelWidth;
36591 if(this.labellg > 0){
36592 label.cls += ' col-lg-' + this.labellg;
36593 items.cls += ' col-lg-' + (12 - this.labellg);
36596 if(this.labelmd > 0){
36597 label.cls += ' col-md-' + this.labelmd;
36598 items.cls += ' col-md-' + (12 - this.labelmd);
36601 if(this.labelsm > 0){
36602 label.cls += ' col-sm-' + this.labelsm;
36603 items.cls += ' col-sm-' + (12 - this.labelsm);
36606 if(this.labelxs > 0){
36607 label.cls += ' col-xs-' + this.labelxs;
36608 items.cls += ' col-xs-' + (12 - this.labelxs);
36614 cls : 'roo-radio-set',
36618 cls : 'roo-radio-set-input',
36621 value : this.value ? this.value : ''
36628 if(this.weight.length){
36629 cfg.cls += ' roo-radio-' + this.weight;
36633 cfg.cls += ' roo-radio-set-inline';
36637 ['xs','sm','md','lg'].map(function(size){
36638 if (settings[size]) {
36639 cfg.cls += ' col-' + size + '-' + settings[size];
36647 initEvents : function()
36649 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36650 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36652 if(!this.fieldLabel.length){
36653 this.labelEl.hide();
36656 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36657 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36659 this.indicator = this.indicatorEl();
36661 if(this.indicator){
36662 this.indicator.addClass('invisible');
36665 this.originalValue = this.getValue();
36669 inputEl: function ()
36671 return this.el.select('.roo-radio-set-input', true).first();
36674 getChildContainer : function()
36676 return this.itemsEl;
36679 register : function(item)
36681 this.radioes.push(item);
36685 validate : function()
36687 if(this.getVisibilityEl().hasClass('hidden')){
36693 Roo.each(this.radioes, function(i){
36702 if(this.allowBlank) {
36706 if(this.disabled || valid){
36711 this.markInvalid();
36716 markValid : function()
36718 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36719 this.indicatorEl().removeClass('visible');
36720 this.indicatorEl().addClass('invisible');
36724 if (Roo.bootstrap.version == 3) {
36725 this.el.removeClass([this.invalidClass, this.validClass]);
36726 this.el.addClass(this.validClass);
36728 this.el.removeClass(['is-invalid','is-valid']);
36729 this.el.addClass(['is-valid']);
36731 this.fireEvent('valid', this);
36734 markInvalid : function(msg)
36736 if(this.allowBlank || this.disabled){
36740 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36741 this.indicatorEl().removeClass('invisible');
36742 this.indicatorEl().addClass('visible');
36744 if (Roo.bootstrap.version == 3) {
36745 this.el.removeClass([this.invalidClass, this.validClass]);
36746 this.el.addClass(this.invalidClass);
36748 this.el.removeClass(['is-invalid','is-valid']);
36749 this.el.addClass(['is-invalid']);
36752 this.fireEvent('invalid', this, msg);
36756 setValue : function(v, suppressEvent)
36758 if(this.value === v){
36765 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36768 Roo.each(this.radioes, function(i){
36770 i.el.removeClass('checked');
36773 Roo.each(this.radioes, function(i){
36775 if(i.value === v || i.value.toString() === v.toString()){
36777 i.el.addClass('checked');
36779 if(suppressEvent !== true){
36780 this.fireEvent('check', this, i);
36791 clearInvalid : function(){
36793 if(!this.el || this.preventMark){
36797 this.el.removeClass([this.invalidClass]);
36799 this.fireEvent('valid', this);
36804 Roo.apply(Roo.bootstrap.RadioSet, {
36808 register : function(set)
36810 this.groups[set.name] = set;
36813 get: function(name)
36815 if (typeof(this.groups[name]) == 'undefined') {
36819 return this.groups[name] ;
36825 * Ext JS Library 1.1.1
36826 * Copyright(c) 2006-2007, Ext JS, LLC.
36828 * Originally Released Under LGPL - original licence link has changed is not relivant.
36831 * <script type="text/javascript">
36836 * @class Roo.bootstrap.SplitBar
36837 * @extends Roo.util.Observable
36838 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36842 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36843 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36844 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36845 split.minSize = 100;
36846 split.maxSize = 600;
36847 split.animate = true;
36848 split.on('moved', splitterMoved);
36851 * Create a new SplitBar
36852 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36853 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36854 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36855 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36856 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36857 position of the SplitBar).
36859 Roo.bootstrap.SplitBar = function(cfg){
36864 // dragElement : elm
36865 // resizingElement: el,
36867 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36868 // placement : Roo.bootstrap.SplitBar.LEFT ,
36869 // existingProxy ???
36872 this.el = Roo.get(cfg.dragElement, true);
36873 this.el.dom.unselectable = "on";
36875 this.resizingEl = Roo.get(cfg.resizingElement, true);
36879 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36880 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36883 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36886 * The minimum size of the resizing element. (Defaults to 0)
36892 * The maximum size of the resizing element. (Defaults to 2000)
36895 this.maxSize = 2000;
36898 * Whether to animate the transition to the new size
36901 this.animate = false;
36904 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36907 this.useShim = false;
36912 if(!cfg.existingProxy){
36914 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36916 this.proxy = Roo.get(cfg.existingProxy).dom;
36919 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36922 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36925 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36928 this.dragSpecs = {};
36931 * @private The adapter to use to positon and resize elements
36933 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36934 this.adapter.init(this);
36936 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36938 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36939 this.el.addClass("roo-splitbar-h");
36942 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36943 this.el.addClass("roo-splitbar-v");
36949 * Fires when the splitter is moved (alias for {@link #event-moved})
36950 * @param {Roo.bootstrap.SplitBar} this
36951 * @param {Number} newSize the new width or height
36956 * Fires when the splitter is moved
36957 * @param {Roo.bootstrap.SplitBar} this
36958 * @param {Number} newSize the new width or height
36962 * @event beforeresize
36963 * Fires before the splitter is dragged
36964 * @param {Roo.bootstrap.SplitBar} this
36966 "beforeresize" : true,
36968 "beforeapply" : true
36971 Roo.util.Observable.call(this);
36974 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36975 onStartProxyDrag : function(x, y){
36976 this.fireEvent("beforeresize", this);
36978 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36980 o.enableDisplayMode("block");
36981 // all splitbars share the same overlay
36982 Roo.bootstrap.SplitBar.prototype.overlay = o;
36984 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36985 this.overlay.show();
36986 Roo.get(this.proxy).setDisplayed("block");
36987 var size = this.adapter.getElementSize(this);
36988 this.activeMinSize = this.getMinimumSize();;
36989 this.activeMaxSize = this.getMaximumSize();;
36990 var c1 = size - this.activeMinSize;
36991 var c2 = Math.max(this.activeMaxSize - size, 0);
36992 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36993 this.dd.resetConstraints();
36994 this.dd.setXConstraint(
36995 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36996 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36998 this.dd.setYConstraint(0, 0);
37000 this.dd.resetConstraints();
37001 this.dd.setXConstraint(0, 0);
37002 this.dd.setYConstraint(
37003 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37004 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37007 this.dragSpecs.startSize = size;
37008 this.dragSpecs.startPoint = [x, y];
37009 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37013 * @private Called after the drag operation by the DDProxy
37015 onEndProxyDrag : function(e){
37016 Roo.get(this.proxy).setDisplayed(false);
37017 var endPoint = Roo.lib.Event.getXY(e);
37019 this.overlay.hide();
37022 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37023 newSize = this.dragSpecs.startSize +
37024 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37025 endPoint[0] - this.dragSpecs.startPoint[0] :
37026 this.dragSpecs.startPoint[0] - endPoint[0]
37029 newSize = this.dragSpecs.startSize +
37030 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37031 endPoint[1] - this.dragSpecs.startPoint[1] :
37032 this.dragSpecs.startPoint[1] - endPoint[1]
37035 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37036 if(newSize != this.dragSpecs.startSize){
37037 if(this.fireEvent('beforeapply', this, newSize) !== false){
37038 this.adapter.setElementSize(this, newSize);
37039 this.fireEvent("moved", this, newSize);
37040 this.fireEvent("resize", this, newSize);
37046 * Get the adapter this SplitBar uses
37047 * @return The adapter object
37049 getAdapter : function(){
37050 return this.adapter;
37054 * Set the adapter this SplitBar uses
37055 * @param {Object} adapter A SplitBar adapter object
37057 setAdapter : function(adapter){
37058 this.adapter = adapter;
37059 this.adapter.init(this);
37063 * Gets the minimum size for the resizing element
37064 * @return {Number} The minimum size
37066 getMinimumSize : function(){
37067 return this.minSize;
37071 * Sets the minimum size for the resizing element
37072 * @param {Number} minSize The minimum size
37074 setMinimumSize : function(minSize){
37075 this.minSize = minSize;
37079 * Gets the maximum size for the resizing element
37080 * @return {Number} The maximum size
37082 getMaximumSize : function(){
37083 return this.maxSize;
37087 * Sets the maximum size for the resizing element
37088 * @param {Number} maxSize The maximum size
37090 setMaximumSize : function(maxSize){
37091 this.maxSize = maxSize;
37095 * Sets the initialize size for the resizing element
37096 * @param {Number} size The initial size
37098 setCurrentSize : function(size){
37099 var oldAnimate = this.animate;
37100 this.animate = false;
37101 this.adapter.setElementSize(this, size);
37102 this.animate = oldAnimate;
37106 * Destroy this splitbar.
37107 * @param {Boolean} removeEl True to remove the element
37109 destroy : function(removeEl){
37111 this.shim.remove();
37114 this.proxy.parentNode.removeChild(this.proxy);
37122 * @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.
37124 Roo.bootstrap.SplitBar.createProxy = function(dir){
37125 var proxy = new Roo.Element(document.createElement("div"));
37126 proxy.unselectable();
37127 var cls = 'roo-splitbar-proxy';
37128 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37129 document.body.appendChild(proxy.dom);
37134 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37135 * Default Adapter. It assumes the splitter and resizing element are not positioned
37136 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37138 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37141 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37142 // do nothing for now
37143 init : function(s){
37147 * Called before drag operations to get the current size of the resizing element.
37148 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37150 getElementSize : function(s){
37151 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37152 return s.resizingEl.getWidth();
37154 return s.resizingEl.getHeight();
37159 * Called after drag operations to set the size of the resizing element.
37160 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37161 * @param {Number} newSize The new size to set
37162 * @param {Function} onComplete A function to be invoked when resizing is complete
37164 setElementSize : function(s, newSize, onComplete){
37165 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37167 s.resizingEl.setWidth(newSize);
37169 onComplete(s, newSize);
37172 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37177 s.resizingEl.setHeight(newSize);
37179 onComplete(s, newSize);
37182 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37189 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37190 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37191 * Adapter that moves the splitter element to align with the resized sizing element.
37192 * Used with an absolute positioned SplitBar.
37193 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37194 * document.body, make sure you assign an id to the body element.
37196 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37197 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37198 this.container = Roo.get(container);
37201 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37202 init : function(s){
37203 this.basic.init(s);
37206 getElementSize : function(s){
37207 return this.basic.getElementSize(s);
37210 setElementSize : function(s, newSize, onComplete){
37211 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37214 moveSplitter : function(s){
37215 var yes = Roo.bootstrap.SplitBar;
37216 switch(s.placement){
37218 s.el.setX(s.resizingEl.getRight());
37221 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37224 s.el.setY(s.resizingEl.getBottom());
37227 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37234 * Orientation constant - Create a vertical SplitBar
37238 Roo.bootstrap.SplitBar.VERTICAL = 1;
37241 * Orientation constant - Create a horizontal SplitBar
37245 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37248 * Placement constant - The resizing element is to the left of the splitter element
37252 Roo.bootstrap.SplitBar.LEFT = 1;
37255 * Placement constant - The resizing element is to the right of the splitter element
37259 Roo.bootstrap.SplitBar.RIGHT = 2;
37262 * Placement constant - The resizing element is positioned above the splitter element
37266 Roo.bootstrap.SplitBar.TOP = 3;
37269 * Placement constant - The resizing element is positioned under splitter element
37273 Roo.bootstrap.SplitBar.BOTTOM = 4;
37274 Roo.namespace("Roo.bootstrap.layout");/*
37276 * Ext JS Library 1.1.1
37277 * Copyright(c) 2006-2007, Ext JS, LLC.
37279 * Originally Released Under LGPL - original licence link has changed is not relivant.
37282 * <script type="text/javascript">
37286 * @class Roo.bootstrap.layout.Manager
37287 * @extends Roo.bootstrap.Component
37288 * Base class for layout managers.
37290 Roo.bootstrap.layout.Manager = function(config)
37292 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37298 /** false to disable window resize monitoring @type Boolean */
37299 this.monitorWindowResize = true;
37304 * Fires when a layout is performed.
37305 * @param {Roo.LayoutManager} this
37309 * @event regionresized
37310 * Fires when the user resizes a region.
37311 * @param {Roo.LayoutRegion} region The resized region
37312 * @param {Number} newSize The new size (width for east/west, height for north/south)
37314 "regionresized" : true,
37316 * @event regioncollapsed
37317 * Fires when a region is collapsed.
37318 * @param {Roo.LayoutRegion} region The collapsed region
37320 "regioncollapsed" : true,
37322 * @event regionexpanded
37323 * Fires when a region is expanded.
37324 * @param {Roo.LayoutRegion} region The expanded region
37326 "regionexpanded" : true
37328 this.updating = false;
37331 this.el = Roo.get(config.el);
37337 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37342 monitorWindowResize : true,
37348 onRender : function(ct, position)
37351 this.el = Roo.get(ct);
37354 //this.fireEvent('render',this);
37358 initEvents: function()
37362 // ie scrollbar fix
37363 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37364 document.body.scroll = "no";
37365 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37366 this.el.position('relative');
37368 this.id = this.el.id;
37369 this.el.addClass("roo-layout-container");
37370 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37371 if(this.el.dom != document.body ) {
37372 this.el.on('resize', this.layout,this);
37373 this.el.on('show', this.layout,this);
37379 * Returns true if this layout is currently being updated
37380 * @return {Boolean}
37382 isUpdating : function(){
37383 return this.updating;
37387 * Suspend the LayoutManager from doing auto-layouts while
37388 * making multiple add or remove calls
37390 beginUpdate : function(){
37391 this.updating = true;
37395 * Restore auto-layouts and optionally disable the manager from performing a layout
37396 * @param {Boolean} noLayout true to disable a layout update
37398 endUpdate : function(noLayout){
37399 this.updating = false;
37405 layout: function(){
37409 onRegionResized : function(region, newSize){
37410 this.fireEvent("regionresized", region, newSize);
37414 onRegionCollapsed : function(region){
37415 this.fireEvent("regioncollapsed", region);
37418 onRegionExpanded : function(region){
37419 this.fireEvent("regionexpanded", region);
37423 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37424 * performs box-model adjustments.
37425 * @return {Object} The size as an object {width: (the width), height: (the height)}
37427 getViewSize : function()
37430 if(this.el.dom != document.body){
37431 size = this.el.getSize();
37433 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37435 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37436 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37441 * Returns the Element this layout is bound to.
37442 * @return {Roo.Element}
37444 getEl : function(){
37449 * Returns the specified region.
37450 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37451 * @return {Roo.LayoutRegion}
37453 getRegion : function(target){
37454 return this.regions[target.toLowerCase()];
37457 onWindowResize : function(){
37458 if(this.monitorWindowResize){
37465 * Ext JS Library 1.1.1
37466 * Copyright(c) 2006-2007, Ext JS, LLC.
37468 * Originally Released Under LGPL - original licence link has changed is not relivant.
37471 * <script type="text/javascript">
37474 * @class Roo.bootstrap.layout.Border
37475 * @extends Roo.bootstrap.layout.Manager
37476 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37477 * please see: examples/bootstrap/nested.html<br><br>
37479 <b>The container the layout is rendered into can be either the body element or any other element.
37480 If it is not the body element, the container needs to either be an absolute positioned element,
37481 or you will need to add "position:relative" to the css of the container. You will also need to specify
37482 the container size if it is not the body element.</b>
37485 * Create a new Border
37486 * @param {Object} config Configuration options
37488 Roo.bootstrap.layout.Border = function(config){
37489 config = config || {};
37490 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37494 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37495 if(config[region]){
37496 config[region].region = region;
37497 this.addRegion(config[region]);
37503 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37505 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37507 parent : false, // this might point to a 'nest' or a ???
37510 * Creates and adds a new region if it doesn't already exist.
37511 * @param {String} target The target region key (north, south, east, west or center).
37512 * @param {Object} config The regions config object
37513 * @return {BorderLayoutRegion} The new region
37515 addRegion : function(config)
37517 if(!this.regions[config.region]){
37518 var r = this.factory(config);
37519 this.bindRegion(r);
37521 return this.regions[config.region];
37525 bindRegion : function(r){
37526 this.regions[r.config.region] = r;
37528 r.on("visibilitychange", this.layout, this);
37529 r.on("paneladded", this.layout, this);
37530 r.on("panelremoved", this.layout, this);
37531 r.on("invalidated", this.layout, this);
37532 r.on("resized", this.onRegionResized, this);
37533 r.on("collapsed", this.onRegionCollapsed, this);
37534 r.on("expanded", this.onRegionExpanded, this);
37538 * Performs a layout update.
37540 layout : function()
37542 if(this.updating) {
37546 // render all the rebions if they have not been done alreayd?
37547 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37548 if(this.regions[region] && !this.regions[region].bodyEl){
37549 this.regions[region].onRender(this.el)
37553 var size = this.getViewSize();
37554 var w = size.width;
37555 var h = size.height;
37560 //var x = 0, y = 0;
37562 var rs = this.regions;
37563 var north = rs["north"];
37564 var south = rs["south"];
37565 var west = rs["west"];
37566 var east = rs["east"];
37567 var center = rs["center"];
37568 //if(this.hideOnLayout){ // not supported anymore
37569 //c.el.setStyle("display", "none");
37571 if(north && north.isVisible()){
37572 var b = north.getBox();
37573 var m = north.getMargins();
37574 b.width = w - (m.left+m.right);
37577 centerY = b.height + b.y + m.bottom;
37578 centerH -= centerY;
37579 north.updateBox(this.safeBox(b));
37581 if(south && south.isVisible()){
37582 var b = south.getBox();
37583 var m = south.getMargins();
37584 b.width = w - (m.left+m.right);
37586 var totalHeight = (b.height + m.top + m.bottom);
37587 b.y = h - totalHeight + m.top;
37588 centerH -= totalHeight;
37589 south.updateBox(this.safeBox(b));
37591 if(west && west.isVisible()){
37592 var b = west.getBox();
37593 var m = west.getMargins();
37594 b.height = centerH - (m.top+m.bottom);
37596 b.y = centerY + m.top;
37597 var totalWidth = (b.width + m.left + m.right);
37598 centerX += totalWidth;
37599 centerW -= totalWidth;
37600 west.updateBox(this.safeBox(b));
37602 if(east && east.isVisible()){
37603 var b = east.getBox();
37604 var m = east.getMargins();
37605 b.height = centerH - (m.top+m.bottom);
37606 var totalWidth = (b.width + m.left + m.right);
37607 b.x = w - totalWidth + m.left;
37608 b.y = centerY + m.top;
37609 centerW -= totalWidth;
37610 east.updateBox(this.safeBox(b));
37613 var m = center.getMargins();
37615 x: centerX + m.left,
37616 y: centerY + m.top,
37617 width: centerW - (m.left+m.right),
37618 height: centerH - (m.top+m.bottom)
37620 //if(this.hideOnLayout){
37621 //center.el.setStyle("display", "block");
37623 center.updateBox(this.safeBox(centerBox));
37626 this.fireEvent("layout", this);
37630 safeBox : function(box){
37631 box.width = Math.max(0, box.width);
37632 box.height = Math.max(0, box.height);
37637 * Adds a ContentPanel (or subclass) to this layout.
37638 * @param {String} target The target region key (north, south, east, west or center).
37639 * @param {Roo.ContentPanel} panel The panel to add
37640 * @return {Roo.ContentPanel} The added panel
37642 add : function(target, panel){
37644 target = target.toLowerCase();
37645 return this.regions[target].add(panel);
37649 * Remove a ContentPanel (or subclass) to this layout.
37650 * @param {String} target The target region key (north, south, east, west or center).
37651 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37652 * @return {Roo.ContentPanel} The removed panel
37654 remove : function(target, panel){
37655 target = target.toLowerCase();
37656 return this.regions[target].remove(panel);
37660 * Searches all regions for a panel with the specified id
37661 * @param {String} panelId
37662 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37664 findPanel : function(panelId){
37665 var rs = this.regions;
37666 for(var target in rs){
37667 if(typeof rs[target] != "function"){
37668 var p = rs[target].getPanel(panelId);
37678 * Searches all regions for a panel with the specified id and activates (shows) it.
37679 * @param {String/ContentPanel} panelId The panels id or the panel itself
37680 * @return {Roo.ContentPanel} The shown panel or null
37682 showPanel : function(panelId) {
37683 var rs = this.regions;
37684 for(var target in rs){
37685 var r = rs[target];
37686 if(typeof r != "function"){
37687 if(r.hasPanel(panelId)){
37688 return r.showPanel(panelId);
37696 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37697 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37700 restoreState : function(provider){
37702 provider = Roo.state.Manager;
37704 var sm = new Roo.LayoutStateManager();
37705 sm.init(this, provider);
37711 * Adds a xtype elements to the layout.
37715 xtype : 'ContentPanel',
37722 xtype : 'NestedLayoutPanel',
37728 items : [ ... list of content panels or nested layout panels.. ]
37732 * @param {Object} cfg Xtype definition of item to add.
37734 addxtype : function(cfg)
37736 // basically accepts a pannel...
37737 // can accept a layout region..!?!?
37738 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37741 // theory? children can only be panels??
37743 //if (!cfg.xtype.match(/Panel$/)) {
37748 if (typeof(cfg.region) == 'undefined') {
37749 Roo.log("Failed to add Panel, region was not set");
37753 var region = cfg.region;
37759 xitems = cfg.items;
37764 if ( region == 'center') {
37765 Roo.log("Center: " + cfg.title);
37771 case 'Content': // ContentPanel (el, cfg)
37772 case 'Scroll': // ContentPanel (el, cfg)
37774 cfg.autoCreate = cfg.autoCreate || true;
37775 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37777 // var el = this.el.createChild();
37778 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37781 this.add(region, ret);
37785 case 'TreePanel': // our new panel!
37786 cfg.el = this.el.createChild();
37787 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37788 this.add(region, ret);
37793 // create a new Layout (which is a Border Layout...
37795 var clayout = cfg.layout;
37796 clayout.el = this.el.createChild();
37797 clayout.items = clayout.items || [];
37801 // replace this exitems with the clayout ones..
37802 xitems = clayout.items;
37804 // force background off if it's in center...
37805 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37806 cfg.background = false;
37808 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37811 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37812 //console.log('adding nested layout panel ' + cfg.toSource());
37813 this.add(region, ret);
37814 nb = {}; /// find first...
37819 // needs grid and region
37821 //var el = this.getRegion(region).el.createChild();
37823 *var el = this.el.createChild();
37824 // create the grid first...
37825 cfg.grid.container = el;
37826 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37829 if (region == 'center' && this.active ) {
37830 cfg.background = false;
37833 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37835 this.add(region, ret);
37837 if (cfg.background) {
37838 // render grid on panel activation (if panel background)
37839 ret.on('activate', function(gp) {
37840 if (!gp.grid.rendered) {
37841 // gp.grid.render(el);
37845 // cfg.grid.render(el);
37851 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37852 // it was the old xcomponent building that caused this before.
37853 // espeically if border is the top element in the tree.
37863 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37865 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37866 this.add(region, ret);
37870 throw "Can not add '" + cfg.xtype + "' to Border";
37876 this.beginUpdate();
37880 Roo.each(xitems, function(i) {
37881 region = nb && i.region ? i.region : false;
37883 var add = ret.addxtype(i);
37886 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37887 if (!i.background) {
37888 abn[region] = nb[region] ;
37895 // make the last non-background panel active..
37896 //if (nb) { Roo.log(abn); }
37899 for(var r in abn) {
37900 region = this.getRegion(r);
37902 // tried using nb[r], but it does not work..
37904 region.showPanel(abn[r]);
37915 factory : function(cfg)
37918 var validRegions = Roo.bootstrap.layout.Border.regions;
37920 var target = cfg.region;
37923 var r = Roo.bootstrap.layout;
37927 return new r.North(cfg);
37929 return new r.South(cfg);
37931 return new r.East(cfg);
37933 return new r.West(cfg);
37935 return new r.Center(cfg);
37937 throw 'Layout region "'+target+'" not supported.';
37944 * Ext JS Library 1.1.1
37945 * Copyright(c) 2006-2007, Ext JS, LLC.
37947 * Originally Released Under LGPL - original licence link has changed is not relivant.
37950 * <script type="text/javascript">
37954 * @class Roo.bootstrap.layout.Basic
37955 * @extends Roo.util.Observable
37956 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37957 * and does not have a titlebar, tabs or any other features. All it does is size and position
37958 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37959 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37960 * @cfg {string} region the region that it inhabits..
37961 * @cfg {bool} skipConfig skip config?
37965 Roo.bootstrap.layout.Basic = function(config){
37967 this.mgr = config.mgr;
37969 this.position = config.region;
37971 var skipConfig = config.skipConfig;
37975 * @scope Roo.BasicLayoutRegion
37979 * @event beforeremove
37980 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37981 * @param {Roo.LayoutRegion} this
37982 * @param {Roo.ContentPanel} panel The panel
37983 * @param {Object} e The cancel event object
37985 "beforeremove" : true,
37987 * @event invalidated
37988 * Fires when the layout for this region is changed.
37989 * @param {Roo.LayoutRegion} this
37991 "invalidated" : true,
37993 * @event visibilitychange
37994 * Fires when this region is shown or hidden
37995 * @param {Roo.LayoutRegion} this
37996 * @param {Boolean} visibility true or false
37998 "visibilitychange" : true,
38000 * @event paneladded
38001 * Fires when a panel is added.
38002 * @param {Roo.LayoutRegion} this
38003 * @param {Roo.ContentPanel} panel The panel
38005 "paneladded" : true,
38007 * @event panelremoved
38008 * Fires when a panel is removed.
38009 * @param {Roo.LayoutRegion} this
38010 * @param {Roo.ContentPanel} panel The panel
38012 "panelremoved" : true,
38014 * @event beforecollapse
38015 * Fires when this region before collapse.
38016 * @param {Roo.LayoutRegion} this
38018 "beforecollapse" : true,
38021 * Fires when this region is collapsed.
38022 * @param {Roo.LayoutRegion} this
38024 "collapsed" : true,
38027 * Fires when this region is expanded.
38028 * @param {Roo.LayoutRegion} this
38033 * Fires when this region is slid into view.
38034 * @param {Roo.LayoutRegion} this
38036 "slideshow" : true,
38039 * Fires when this region slides out of view.
38040 * @param {Roo.LayoutRegion} this
38042 "slidehide" : true,
38044 * @event panelactivated
38045 * Fires when a panel is activated.
38046 * @param {Roo.LayoutRegion} this
38047 * @param {Roo.ContentPanel} panel The activated panel
38049 "panelactivated" : true,
38052 * Fires when the user resizes this region.
38053 * @param {Roo.LayoutRegion} this
38054 * @param {Number} newSize The new size (width for east/west, height for north/south)
38058 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38059 this.panels = new Roo.util.MixedCollection();
38060 this.panels.getKey = this.getPanelId.createDelegate(this);
38062 this.activePanel = null;
38063 // ensure listeners are added...
38065 if (config.listeners || config.events) {
38066 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38067 listeners : config.listeners || {},
38068 events : config.events || {}
38072 if(skipConfig !== true){
38073 this.applyConfig(config);
38077 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38079 getPanelId : function(p){
38083 applyConfig : function(config){
38084 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38085 this.config = config;
38090 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38091 * the width, for horizontal (north, south) the height.
38092 * @param {Number} newSize The new width or height
38094 resizeTo : function(newSize){
38095 var el = this.el ? this.el :
38096 (this.activePanel ? this.activePanel.getEl() : null);
38098 switch(this.position){
38101 el.setWidth(newSize);
38102 this.fireEvent("resized", this, newSize);
38106 el.setHeight(newSize);
38107 this.fireEvent("resized", this, newSize);
38113 getBox : function(){
38114 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38117 getMargins : function(){
38118 return this.margins;
38121 updateBox : function(box){
38123 var el = this.activePanel.getEl();
38124 el.dom.style.left = box.x + "px";
38125 el.dom.style.top = box.y + "px";
38126 this.activePanel.setSize(box.width, box.height);
38130 * Returns the container element for this region.
38131 * @return {Roo.Element}
38133 getEl : function(){
38134 return this.activePanel;
38138 * Returns true if this region is currently visible.
38139 * @return {Boolean}
38141 isVisible : function(){
38142 return this.activePanel ? true : false;
38145 setActivePanel : function(panel){
38146 panel = this.getPanel(panel);
38147 if(this.activePanel && this.activePanel != panel){
38148 this.activePanel.setActiveState(false);
38149 this.activePanel.getEl().setLeftTop(-10000,-10000);
38151 this.activePanel = panel;
38152 panel.setActiveState(true);
38154 panel.setSize(this.box.width, this.box.height);
38156 this.fireEvent("panelactivated", this, panel);
38157 this.fireEvent("invalidated");
38161 * Show the specified panel.
38162 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38163 * @return {Roo.ContentPanel} The shown panel or null
38165 showPanel : function(panel){
38166 panel = this.getPanel(panel);
38168 this.setActivePanel(panel);
38174 * Get the active panel for this region.
38175 * @return {Roo.ContentPanel} The active panel or null
38177 getActivePanel : function(){
38178 return this.activePanel;
38182 * Add the passed ContentPanel(s)
38183 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38184 * @return {Roo.ContentPanel} The panel added (if only one was added)
38186 add : function(panel){
38187 if(arguments.length > 1){
38188 for(var i = 0, len = arguments.length; i < len; i++) {
38189 this.add(arguments[i]);
38193 if(this.hasPanel(panel)){
38194 this.showPanel(panel);
38197 var el = panel.getEl();
38198 if(el.dom.parentNode != this.mgr.el.dom){
38199 this.mgr.el.dom.appendChild(el.dom);
38201 if(panel.setRegion){
38202 panel.setRegion(this);
38204 this.panels.add(panel);
38205 el.setStyle("position", "absolute");
38206 if(!panel.background){
38207 this.setActivePanel(panel);
38208 if(this.config.initialSize && this.panels.getCount()==1){
38209 this.resizeTo(this.config.initialSize);
38212 this.fireEvent("paneladded", this, panel);
38217 * Returns true if the panel is in this region.
38218 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38219 * @return {Boolean}
38221 hasPanel : function(panel){
38222 if(typeof panel == "object"){ // must be panel obj
38223 panel = panel.getId();
38225 return this.getPanel(panel) ? true : false;
38229 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38230 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38231 * @param {Boolean} preservePanel Overrides the config preservePanel option
38232 * @return {Roo.ContentPanel} The panel that was removed
38234 remove : function(panel, preservePanel){
38235 panel = this.getPanel(panel);
38240 this.fireEvent("beforeremove", this, panel, e);
38241 if(e.cancel === true){
38244 var panelId = panel.getId();
38245 this.panels.removeKey(panelId);
38250 * Returns the panel specified or null if it's not in this region.
38251 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38252 * @return {Roo.ContentPanel}
38254 getPanel : function(id){
38255 if(typeof id == "object"){ // must be panel obj
38258 return this.panels.get(id);
38262 * Returns this regions position (north/south/east/west/center).
38265 getPosition: function(){
38266 return this.position;
38270 * Ext JS Library 1.1.1
38271 * Copyright(c) 2006-2007, Ext JS, LLC.
38273 * Originally Released Under LGPL - original licence link has changed is not relivant.
38276 * <script type="text/javascript">
38280 * @class Roo.bootstrap.layout.Region
38281 * @extends Roo.bootstrap.layout.Basic
38282 * This class represents a region in a layout manager.
38284 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38285 * @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})
38286 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38287 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38288 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38289 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38290 * @cfg {String} title The title for the region (overrides panel titles)
38291 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38292 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38293 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38294 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38295 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38296 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38297 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38298 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38299 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38300 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38302 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38303 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38304 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38305 * @cfg {Number} width For East/West panels
38306 * @cfg {Number} height For North/South panels
38307 * @cfg {Boolean} split To show the splitter
38308 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38310 * @cfg {string} cls Extra CSS classes to add to region
38312 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38313 * @cfg {string} region the region that it inhabits..
38316 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38317 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38319 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38320 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38321 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38323 Roo.bootstrap.layout.Region = function(config)
38325 this.applyConfig(config);
38327 var mgr = config.mgr;
38328 var pos = config.region;
38329 config.skipConfig = true;
38330 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38333 this.onRender(mgr.el);
38336 this.visible = true;
38337 this.collapsed = false;
38338 this.unrendered_panels = [];
38341 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38343 position: '', // set by wrapper (eg. north/south etc..)
38344 unrendered_panels : null, // unrendered panels.
38346 tabPosition : false,
38348 mgr: false, // points to 'Border'
38351 createBody : function(){
38352 /** This region's body element
38353 * @type Roo.Element */
38354 this.bodyEl = this.el.createChild({
38356 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38360 onRender: function(ctr, pos)
38362 var dh = Roo.DomHelper;
38363 /** This region's container element
38364 * @type Roo.Element */
38365 this.el = dh.append(ctr.dom, {
38367 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38369 /** This region's title element
38370 * @type Roo.Element */
38372 this.titleEl = dh.append(this.el.dom, {
38374 unselectable: "on",
38375 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38377 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38378 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38382 this.titleEl.enableDisplayMode();
38383 /** This region's title text element
38384 * @type HTMLElement */
38385 this.titleTextEl = this.titleEl.dom.firstChild;
38386 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38388 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38389 this.closeBtn.enableDisplayMode();
38390 this.closeBtn.on("click", this.closeClicked, this);
38391 this.closeBtn.hide();
38393 this.createBody(this.config);
38394 if(this.config.hideWhenEmpty){
38396 this.on("paneladded", this.validateVisibility, this);
38397 this.on("panelremoved", this.validateVisibility, this);
38399 if(this.autoScroll){
38400 this.bodyEl.setStyle("overflow", "auto");
38402 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38404 //if(c.titlebar !== false){
38405 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38406 this.titleEl.hide();
38408 this.titleEl.show();
38409 if(this.config.title){
38410 this.titleTextEl.innerHTML = this.config.title;
38414 if(this.config.collapsed){
38415 this.collapse(true);
38417 if(this.config.hidden){
38421 if (this.unrendered_panels && this.unrendered_panels.length) {
38422 for (var i =0;i< this.unrendered_panels.length; i++) {
38423 this.add(this.unrendered_panels[i]);
38425 this.unrendered_panels = null;
38431 applyConfig : function(c)
38434 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38435 var dh = Roo.DomHelper;
38436 if(c.titlebar !== false){
38437 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38438 this.collapseBtn.on("click", this.collapse, this);
38439 this.collapseBtn.enableDisplayMode();
38441 if(c.showPin === true || this.showPin){
38442 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38443 this.stickBtn.enableDisplayMode();
38444 this.stickBtn.on("click", this.expand, this);
38445 this.stickBtn.hide();
38450 /** This region's collapsed element
38451 * @type Roo.Element */
38454 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38455 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38458 if(c.floatable !== false){
38459 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38460 this.collapsedEl.on("click", this.collapseClick, this);
38463 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38464 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38465 id: "message", unselectable: "on", style:{"float":"left"}});
38466 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38468 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38469 this.expandBtn.on("click", this.expand, this);
38473 if(this.collapseBtn){
38474 this.collapseBtn.setVisible(c.collapsible == true);
38477 this.cmargins = c.cmargins || this.cmargins ||
38478 (this.position == "west" || this.position == "east" ?
38479 {top: 0, left: 2, right:2, bottom: 0} :
38480 {top: 2, left: 0, right:0, bottom: 2});
38482 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38485 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38487 this.autoScroll = c.autoScroll || false;
38492 this.duration = c.duration || .30;
38493 this.slideDuration = c.slideDuration || .45;
38498 * Returns true if this region is currently visible.
38499 * @return {Boolean}
38501 isVisible : function(){
38502 return this.visible;
38506 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38507 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38509 //setCollapsedTitle : function(title){
38510 // title = title || " ";
38511 // if(this.collapsedTitleTextEl){
38512 // this.collapsedTitleTextEl.innerHTML = title;
38516 getBox : function(){
38518 // if(!this.collapsed){
38519 b = this.el.getBox(false, true);
38521 // b = this.collapsedEl.getBox(false, true);
38526 getMargins : function(){
38527 return this.margins;
38528 //return this.collapsed ? this.cmargins : this.margins;
38531 highlight : function(){
38532 this.el.addClass("x-layout-panel-dragover");
38535 unhighlight : function(){
38536 this.el.removeClass("x-layout-panel-dragover");
38539 updateBox : function(box)
38541 if (!this.bodyEl) {
38542 return; // not rendered yet..
38546 if(!this.collapsed){
38547 this.el.dom.style.left = box.x + "px";
38548 this.el.dom.style.top = box.y + "px";
38549 this.updateBody(box.width, box.height);
38551 this.collapsedEl.dom.style.left = box.x + "px";
38552 this.collapsedEl.dom.style.top = box.y + "px";
38553 this.collapsedEl.setSize(box.width, box.height);
38556 this.tabs.autoSizeTabs();
38560 updateBody : function(w, h)
38563 this.el.setWidth(w);
38564 w -= this.el.getBorderWidth("rl");
38565 if(this.config.adjustments){
38566 w += this.config.adjustments[0];
38569 if(h !== null && h > 0){
38570 this.el.setHeight(h);
38571 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38572 h -= this.el.getBorderWidth("tb");
38573 if(this.config.adjustments){
38574 h += this.config.adjustments[1];
38576 this.bodyEl.setHeight(h);
38578 h = this.tabs.syncHeight(h);
38581 if(this.panelSize){
38582 w = w !== null ? w : this.panelSize.width;
38583 h = h !== null ? h : this.panelSize.height;
38585 if(this.activePanel){
38586 var el = this.activePanel.getEl();
38587 w = w !== null ? w : el.getWidth();
38588 h = h !== null ? h : el.getHeight();
38589 this.panelSize = {width: w, height: h};
38590 this.activePanel.setSize(w, h);
38592 if(Roo.isIE && this.tabs){
38593 this.tabs.el.repaint();
38598 * Returns the container element for this region.
38599 * @return {Roo.Element}
38601 getEl : function(){
38606 * Hides this region.
38609 //if(!this.collapsed){
38610 this.el.dom.style.left = "-2000px";
38613 // this.collapsedEl.dom.style.left = "-2000px";
38614 // this.collapsedEl.hide();
38616 this.visible = false;
38617 this.fireEvent("visibilitychange", this, false);
38621 * Shows this region if it was previously hidden.
38624 //if(!this.collapsed){
38627 // this.collapsedEl.show();
38629 this.visible = true;
38630 this.fireEvent("visibilitychange", this, true);
38633 closeClicked : function(){
38634 if(this.activePanel){
38635 this.remove(this.activePanel);
38639 collapseClick : function(e){
38641 e.stopPropagation();
38644 e.stopPropagation();
38650 * Collapses this region.
38651 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38654 collapse : function(skipAnim, skipCheck = false){
38655 if(this.collapsed) {
38659 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38661 this.collapsed = true;
38663 this.split.el.hide();
38665 if(this.config.animate && skipAnim !== true){
38666 this.fireEvent("invalidated", this);
38667 this.animateCollapse();
38669 this.el.setLocation(-20000,-20000);
38671 this.collapsedEl.show();
38672 this.fireEvent("collapsed", this);
38673 this.fireEvent("invalidated", this);
38679 animateCollapse : function(){
38684 * Expands this region if it was previously collapsed.
38685 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38686 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38689 expand : function(e, skipAnim){
38691 e.stopPropagation();
38693 if(!this.collapsed || this.el.hasActiveFx()) {
38697 this.afterSlideIn();
38700 this.collapsed = false;
38701 if(this.config.animate && skipAnim !== true){
38702 this.animateExpand();
38706 this.split.el.show();
38708 this.collapsedEl.setLocation(-2000,-2000);
38709 this.collapsedEl.hide();
38710 this.fireEvent("invalidated", this);
38711 this.fireEvent("expanded", this);
38715 animateExpand : function(){
38719 initTabs : function()
38721 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38723 var ts = new Roo.bootstrap.panel.Tabs({
38724 el: this.bodyEl.dom,
38726 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38727 disableTooltips: this.config.disableTabTips,
38728 toolbar : this.config.toolbar
38731 if(this.config.hideTabs){
38732 ts.stripWrap.setDisplayed(false);
38735 ts.resizeTabs = this.config.resizeTabs === true;
38736 ts.minTabWidth = this.config.minTabWidth || 40;
38737 ts.maxTabWidth = this.config.maxTabWidth || 250;
38738 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38739 ts.monitorResize = false;
38740 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38741 ts.bodyEl.addClass('roo-layout-tabs-body');
38742 this.panels.each(this.initPanelAsTab, this);
38745 initPanelAsTab : function(panel){
38746 var ti = this.tabs.addTab(
38750 this.config.closeOnTab && panel.isClosable(),
38753 if(panel.tabTip !== undefined){
38754 ti.setTooltip(panel.tabTip);
38756 ti.on("activate", function(){
38757 this.setActivePanel(panel);
38760 if(this.config.closeOnTab){
38761 ti.on("beforeclose", function(t, e){
38763 this.remove(panel);
38767 panel.tabItem = ti;
38772 updatePanelTitle : function(panel, title)
38774 if(this.activePanel == panel){
38775 this.updateTitle(title);
38778 var ti = this.tabs.getTab(panel.getEl().id);
38780 if(panel.tabTip !== undefined){
38781 ti.setTooltip(panel.tabTip);
38786 updateTitle : function(title){
38787 if(this.titleTextEl && !this.config.title){
38788 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38792 setActivePanel : function(panel)
38794 panel = this.getPanel(panel);
38795 if(this.activePanel && this.activePanel != panel){
38796 if(this.activePanel.setActiveState(false) === false){
38800 this.activePanel = panel;
38801 panel.setActiveState(true);
38802 if(this.panelSize){
38803 panel.setSize(this.panelSize.width, this.panelSize.height);
38806 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38808 this.updateTitle(panel.getTitle());
38810 this.fireEvent("invalidated", this);
38812 this.fireEvent("panelactivated", this, panel);
38816 * Shows the specified panel.
38817 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38818 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38820 showPanel : function(panel)
38822 panel = this.getPanel(panel);
38825 var tab = this.tabs.getTab(panel.getEl().id);
38826 if(tab.isHidden()){
38827 this.tabs.unhideTab(tab.id);
38831 this.setActivePanel(panel);
38838 * Get the active panel for this region.
38839 * @return {Roo.ContentPanel} The active panel or null
38841 getActivePanel : function(){
38842 return this.activePanel;
38845 validateVisibility : function(){
38846 if(this.panels.getCount() < 1){
38847 this.updateTitle(" ");
38848 this.closeBtn.hide();
38851 if(!this.isVisible()){
38858 * Adds the passed ContentPanel(s) to this region.
38859 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38860 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38862 add : function(panel)
38864 if(arguments.length > 1){
38865 for(var i = 0, len = arguments.length; i < len; i++) {
38866 this.add(arguments[i]);
38871 // if we have not been rendered yet, then we can not really do much of this..
38872 if (!this.bodyEl) {
38873 this.unrendered_panels.push(panel);
38880 if(this.hasPanel(panel)){
38881 this.showPanel(panel);
38884 panel.setRegion(this);
38885 this.panels.add(panel);
38886 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38887 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38888 // and hide them... ???
38889 this.bodyEl.dom.appendChild(panel.getEl().dom);
38890 if(panel.background !== true){
38891 this.setActivePanel(panel);
38893 this.fireEvent("paneladded", this, panel);
38900 this.initPanelAsTab(panel);
38904 if(panel.background !== true){
38905 this.tabs.activate(panel.getEl().id);
38907 this.fireEvent("paneladded", this, panel);
38912 * Hides the tab for the specified panel.
38913 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38915 hidePanel : function(panel){
38916 if(this.tabs && (panel = this.getPanel(panel))){
38917 this.tabs.hideTab(panel.getEl().id);
38922 * Unhides the tab for a previously hidden panel.
38923 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38925 unhidePanel : function(panel){
38926 if(this.tabs && (panel = this.getPanel(panel))){
38927 this.tabs.unhideTab(panel.getEl().id);
38931 clearPanels : function(){
38932 while(this.panels.getCount() > 0){
38933 this.remove(this.panels.first());
38938 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38939 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38940 * @param {Boolean} preservePanel Overrides the config preservePanel option
38941 * @return {Roo.ContentPanel} The panel that was removed
38943 remove : function(panel, preservePanel)
38945 panel = this.getPanel(panel);
38950 this.fireEvent("beforeremove", this, panel, e);
38951 if(e.cancel === true){
38954 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38955 var panelId = panel.getId();
38956 this.panels.removeKey(panelId);
38958 document.body.appendChild(panel.getEl().dom);
38961 this.tabs.removeTab(panel.getEl().id);
38962 }else if (!preservePanel){
38963 this.bodyEl.dom.removeChild(panel.getEl().dom);
38965 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38966 var p = this.panels.first();
38967 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38968 tempEl.appendChild(p.getEl().dom);
38969 this.bodyEl.update("");
38970 this.bodyEl.dom.appendChild(p.getEl().dom);
38972 this.updateTitle(p.getTitle());
38974 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38975 this.setActivePanel(p);
38977 panel.setRegion(null);
38978 if(this.activePanel == panel){
38979 this.activePanel = null;
38981 if(this.config.autoDestroy !== false && preservePanel !== true){
38982 try{panel.destroy();}catch(e){}
38984 this.fireEvent("panelremoved", this, panel);
38989 * Returns the TabPanel component used by this region
38990 * @return {Roo.TabPanel}
38992 getTabs : function(){
38996 createTool : function(parentEl, className){
38997 var btn = Roo.DomHelper.append(parentEl, {
38999 cls: "x-layout-tools-button",
39002 cls: "roo-layout-tools-button-inner " + className,
39006 btn.addClassOnOver("roo-layout-tools-button-over");
39011 * Ext JS Library 1.1.1
39012 * Copyright(c) 2006-2007, Ext JS, LLC.
39014 * Originally Released Under LGPL - original licence link has changed is not relivant.
39017 * <script type="text/javascript">
39023 * @class Roo.SplitLayoutRegion
39024 * @extends Roo.LayoutRegion
39025 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39027 Roo.bootstrap.layout.Split = function(config){
39028 this.cursor = config.cursor;
39029 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39032 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39034 splitTip : "Drag to resize.",
39035 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39036 useSplitTips : false,
39038 applyConfig : function(config){
39039 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39042 onRender : function(ctr,pos) {
39044 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39045 if(!this.config.split){
39050 var splitEl = Roo.DomHelper.append(ctr.dom, {
39052 id: this.el.id + "-split",
39053 cls: "roo-layout-split roo-layout-split-"+this.position,
39056 /** The SplitBar for this region
39057 * @type Roo.SplitBar */
39058 // does not exist yet...
39059 Roo.log([this.position, this.orientation]);
39061 this.split = new Roo.bootstrap.SplitBar({
39062 dragElement : splitEl,
39063 resizingElement: this.el,
39064 orientation : this.orientation
39067 this.split.on("moved", this.onSplitMove, this);
39068 this.split.useShim = this.config.useShim === true;
39069 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39070 if(this.useSplitTips){
39071 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39073 //if(config.collapsible){
39074 // this.split.el.on("dblclick", this.collapse, this);
39077 if(typeof this.config.minSize != "undefined"){
39078 this.split.minSize = this.config.minSize;
39080 if(typeof this.config.maxSize != "undefined"){
39081 this.split.maxSize = this.config.maxSize;
39083 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39084 this.hideSplitter();
39089 getHMaxSize : function(){
39090 var cmax = this.config.maxSize || 10000;
39091 var center = this.mgr.getRegion("center");
39092 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39095 getVMaxSize : function(){
39096 var cmax = this.config.maxSize || 10000;
39097 var center = this.mgr.getRegion("center");
39098 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39101 onSplitMove : function(split, newSize){
39102 this.fireEvent("resized", this, newSize);
39106 * Returns the {@link Roo.SplitBar} for this region.
39107 * @return {Roo.SplitBar}
39109 getSplitBar : function(){
39114 this.hideSplitter();
39115 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39118 hideSplitter : function(){
39120 this.split.el.setLocation(-2000,-2000);
39121 this.split.el.hide();
39127 this.split.el.show();
39129 Roo.bootstrap.layout.Split.superclass.show.call(this);
39132 beforeSlide: function(){
39133 if(Roo.isGecko){// firefox overflow auto bug workaround
39134 this.bodyEl.clip();
39136 this.tabs.bodyEl.clip();
39138 if(this.activePanel){
39139 this.activePanel.getEl().clip();
39141 if(this.activePanel.beforeSlide){
39142 this.activePanel.beforeSlide();
39148 afterSlide : function(){
39149 if(Roo.isGecko){// firefox overflow auto bug workaround
39150 this.bodyEl.unclip();
39152 this.tabs.bodyEl.unclip();
39154 if(this.activePanel){
39155 this.activePanel.getEl().unclip();
39156 if(this.activePanel.afterSlide){
39157 this.activePanel.afterSlide();
39163 initAutoHide : function(){
39164 if(this.autoHide !== false){
39165 if(!this.autoHideHd){
39166 var st = new Roo.util.DelayedTask(this.slideIn, this);
39167 this.autoHideHd = {
39168 "mouseout": function(e){
39169 if(!e.within(this.el, true)){
39173 "mouseover" : function(e){
39179 this.el.on(this.autoHideHd);
39183 clearAutoHide : function(){
39184 if(this.autoHide !== false){
39185 this.el.un("mouseout", this.autoHideHd.mouseout);
39186 this.el.un("mouseover", this.autoHideHd.mouseover);
39190 clearMonitor : function(){
39191 Roo.get(document).un("click", this.slideInIf, this);
39194 // these names are backwards but not changed for compat
39195 slideOut : function(){
39196 if(this.isSlid || this.el.hasActiveFx()){
39199 this.isSlid = true;
39200 if(this.collapseBtn){
39201 this.collapseBtn.hide();
39203 this.closeBtnState = this.closeBtn.getStyle('display');
39204 this.closeBtn.hide();
39206 this.stickBtn.show();
39209 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39210 this.beforeSlide();
39211 this.el.setStyle("z-index", 10001);
39212 this.el.slideIn(this.getSlideAnchor(), {
39213 callback: function(){
39215 this.initAutoHide();
39216 Roo.get(document).on("click", this.slideInIf, this);
39217 this.fireEvent("slideshow", this);
39224 afterSlideIn : function(){
39225 this.clearAutoHide();
39226 this.isSlid = false;
39227 this.clearMonitor();
39228 this.el.setStyle("z-index", "");
39229 if(this.collapseBtn){
39230 this.collapseBtn.show();
39232 this.closeBtn.setStyle('display', this.closeBtnState);
39234 this.stickBtn.hide();
39236 this.fireEvent("slidehide", this);
39239 slideIn : function(cb){
39240 if(!this.isSlid || this.el.hasActiveFx()){
39244 this.isSlid = false;
39245 this.beforeSlide();
39246 this.el.slideOut(this.getSlideAnchor(), {
39247 callback: function(){
39248 this.el.setLeftTop(-10000, -10000);
39250 this.afterSlideIn();
39258 slideInIf : function(e){
39259 if(!e.within(this.el)){
39264 animateCollapse : function(){
39265 this.beforeSlide();
39266 this.el.setStyle("z-index", 20000);
39267 var anchor = this.getSlideAnchor();
39268 this.el.slideOut(anchor, {
39269 callback : function(){
39270 this.el.setStyle("z-index", "");
39271 this.collapsedEl.slideIn(anchor, {duration:.3});
39273 this.el.setLocation(-10000,-10000);
39275 this.fireEvent("collapsed", this);
39282 animateExpand : function(){
39283 this.beforeSlide();
39284 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39285 this.el.setStyle("z-index", 20000);
39286 this.collapsedEl.hide({
39289 this.el.slideIn(this.getSlideAnchor(), {
39290 callback : function(){
39291 this.el.setStyle("z-index", "");
39294 this.split.el.show();
39296 this.fireEvent("invalidated", this);
39297 this.fireEvent("expanded", this);
39325 getAnchor : function(){
39326 return this.anchors[this.position];
39329 getCollapseAnchor : function(){
39330 return this.canchors[this.position];
39333 getSlideAnchor : function(){
39334 return this.sanchors[this.position];
39337 getAlignAdj : function(){
39338 var cm = this.cmargins;
39339 switch(this.position){
39355 getExpandAdj : function(){
39356 var c = this.collapsedEl, cm = this.cmargins;
39357 switch(this.position){
39359 return [-(cm.right+c.getWidth()+cm.left), 0];
39362 return [cm.right+c.getWidth()+cm.left, 0];
39365 return [0, -(cm.top+cm.bottom+c.getHeight())];
39368 return [0, cm.top+cm.bottom+c.getHeight()];
39374 * Ext JS Library 1.1.1
39375 * Copyright(c) 2006-2007, Ext JS, LLC.
39377 * Originally Released Under LGPL - original licence link has changed is not relivant.
39380 * <script type="text/javascript">
39383 * These classes are private internal classes
39385 Roo.bootstrap.layout.Center = function(config){
39386 config.region = "center";
39387 Roo.bootstrap.layout.Region.call(this, config);
39388 this.visible = true;
39389 this.minWidth = config.minWidth || 20;
39390 this.minHeight = config.minHeight || 20;
39393 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39395 // center panel can't be hidden
39399 // center panel can't be hidden
39402 getMinWidth: function(){
39403 return this.minWidth;
39406 getMinHeight: function(){
39407 return this.minHeight;
39421 Roo.bootstrap.layout.North = function(config)
39423 config.region = 'north';
39424 config.cursor = 'n-resize';
39426 Roo.bootstrap.layout.Split.call(this, config);
39430 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39431 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39432 this.split.el.addClass("roo-layout-split-v");
39434 //var size = config.initialSize || config.height;
39435 //if(this.el && typeof size != "undefined"){
39436 // this.el.setHeight(size);
39439 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39441 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39444 onRender : function(ctr, pos)
39446 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39447 var size = this.config.initialSize || this.config.height;
39448 if(this.el && typeof size != "undefined"){
39449 this.el.setHeight(size);
39454 getBox : function(){
39455 if(this.collapsed){
39456 return this.collapsedEl.getBox();
39458 var box = this.el.getBox();
39460 box.height += this.split.el.getHeight();
39465 updateBox : function(box){
39466 if(this.split && !this.collapsed){
39467 box.height -= this.split.el.getHeight();
39468 this.split.el.setLeft(box.x);
39469 this.split.el.setTop(box.y+box.height);
39470 this.split.el.setWidth(box.width);
39472 if(this.collapsed){
39473 this.updateBody(box.width, null);
39475 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39483 Roo.bootstrap.layout.South = function(config){
39484 config.region = 'south';
39485 config.cursor = 's-resize';
39486 Roo.bootstrap.layout.Split.call(this, config);
39488 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39489 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39490 this.split.el.addClass("roo-layout-split-v");
39495 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39496 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39498 onRender : function(ctr, pos)
39500 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39501 var size = this.config.initialSize || this.config.height;
39502 if(this.el && typeof size != "undefined"){
39503 this.el.setHeight(size);
39508 getBox : function(){
39509 if(this.collapsed){
39510 return this.collapsedEl.getBox();
39512 var box = this.el.getBox();
39514 var sh = this.split.el.getHeight();
39521 updateBox : function(box){
39522 if(this.split && !this.collapsed){
39523 var sh = this.split.el.getHeight();
39526 this.split.el.setLeft(box.x);
39527 this.split.el.setTop(box.y-sh);
39528 this.split.el.setWidth(box.width);
39530 if(this.collapsed){
39531 this.updateBody(box.width, null);
39533 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39537 Roo.bootstrap.layout.East = function(config){
39538 config.region = "east";
39539 config.cursor = "e-resize";
39540 Roo.bootstrap.layout.Split.call(this, config);
39542 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39543 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39544 this.split.el.addClass("roo-layout-split-h");
39548 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39549 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39551 onRender : function(ctr, pos)
39553 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39554 var size = this.config.initialSize || this.config.width;
39555 if(this.el && typeof size != "undefined"){
39556 this.el.setWidth(size);
39561 getBox : function(){
39562 if(this.collapsed){
39563 return this.collapsedEl.getBox();
39565 var box = this.el.getBox();
39567 var sw = this.split.el.getWidth();
39574 updateBox : function(box){
39575 if(this.split && !this.collapsed){
39576 var sw = this.split.el.getWidth();
39578 this.split.el.setLeft(box.x);
39579 this.split.el.setTop(box.y);
39580 this.split.el.setHeight(box.height);
39583 if(this.collapsed){
39584 this.updateBody(null, box.height);
39586 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39590 Roo.bootstrap.layout.West = function(config){
39591 config.region = "west";
39592 config.cursor = "w-resize";
39594 Roo.bootstrap.layout.Split.call(this, config);
39596 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39597 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39598 this.split.el.addClass("roo-layout-split-h");
39602 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39603 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39605 onRender: function(ctr, pos)
39607 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39608 var size = this.config.initialSize || this.config.width;
39609 if(typeof size != "undefined"){
39610 this.el.setWidth(size);
39614 getBox : function(){
39615 if(this.collapsed){
39616 return this.collapsedEl.getBox();
39618 var box = this.el.getBox();
39619 if (box.width == 0) {
39620 box.width = this.config.width; // kludge?
39623 box.width += this.split.el.getWidth();
39628 updateBox : function(box){
39629 if(this.split && !this.collapsed){
39630 var sw = this.split.el.getWidth();
39632 this.split.el.setLeft(box.x+box.width);
39633 this.split.el.setTop(box.y);
39634 this.split.el.setHeight(box.height);
39636 if(this.collapsed){
39637 this.updateBody(null, box.height);
39639 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39641 });Roo.namespace("Roo.bootstrap.panel");/*
39643 * Ext JS Library 1.1.1
39644 * Copyright(c) 2006-2007, Ext JS, LLC.
39646 * Originally Released Under LGPL - original licence link has changed is not relivant.
39649 * <script type="text/javascript">
39652 * @class Roo.ContentPanel
39653 * @extends Roo.util.Observable
39654 * A basic ContentPanel element.
39655 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39656 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39657 * @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
39658 * @cfg {Boolean} closable True if the panel can be closed/removed
39659 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39660 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39661 * @cfg {Toolbar} toolbar A toolbar for this panel
39662 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39663 * @cfg {String} title The title for this panel
39664 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39665 * @cfg {String} url Calls {@link #setUrl} with this value
39666 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39667 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39668 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39669 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39670 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39671 * @cfg {Boolean} badges render the badges
39672 * @cfg {String} cls extra classes to use
39673 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39676 * Create a new ContentPanel.
39677 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39678 * @param {String/Object} config A string to set only the title or a config object
39679 * @param {String} content (optional) Set the HTML content for this panel
39680 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39682 Roo.bootstrap.panel.Content = function( config){
39684 this.tpl = config.tpl || false;
39686 var el = config.el;
39687 var content = config.content;
39689 if(config.autoCreate){ // xtype is available if this is called from factory
39692 this.el = Roo.get(el);
39693 if(!this.el && config && config.autoCreate){
39694 if(typeof config.autoCreate == "object"){
39695 if(!config.autoCreate.id){
39696 config.autoCreate.id = config.id||el;
39698 this.el = Roo.DomHelper.append(document.body,
39699 config.autoCreate, true);
39703 cls: (config.cls || '') +
39704 (config.background ? ' bg-' + config.background : '') +
39705 " roo-layout-inactive-content",
39708 if (config.iframe) {
39712 style : 'border: 0px',
39713 src : 'about:blank'
39719 elcfg.html = config.html;
39723 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39724 if (config.iframe) {
39725 this.iframeEl = this.el.select('iframe',true).first();
39730 this.closable = false;
39731 this.loaded = false;
39732 this.active = false;
39735 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39737 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39739 this.wrapEl = this.el; //this.el.wrap();
39741 if (config.toolbar.items) {
39742 ti = config.toolbar.items ;
39743 delete config.toolbar.items ;
39747 this.toolbar.render(this.wrapEl, 'before');
39748 for(var i =0;i < ti.length;i++) {
39749 // Roo.log(['add child', items[i]]);
39750 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39752 this.toolbar.items = nitems;
39753 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39754 delete config.toolbar;
39758 // xtype created footer. - not sure if will work as we normally have to render first..
39759 if (this.footer && !this.footer.el && this.footer.xtype) {
39760 if (!this.wrapEl) {
39761 this.wrapEl = this.el.wrap();
39764 this.footer.container = this.wrapEl.createChild();
39766 this.footer = Roo.factory(this.footer, Roo);
39771 if(typeof config == "string"){
39772 this.title = config;
39774 Roo.apply(this, config);
39778 this.resizeEl = Roo.get(this.resizeEl, true);
39780 this.resizeEl = this.el;
39782 // handle view.xtype
39790 * Fires when this panel is activated.
39791 * @param {Roo.ContentPanel} this
39795 * @event deactivate
39796 * Fires when this panel is activated.
39797 * @param {Roo.ContentPanel} this
39799 "deactivate" : true,
39803 * Fires when this panel is resized if fitToFrame is true.
39804 * @param {Roo.ContentPanel} this
39805 * @param {Number} width The width after any component adjustments
39806 * @param {Number} height The height after any component adjustments
39812 * Fires when this tab is created
39813 * @param {Roo.ContentPanel} this
39824 if(this.autoScroll && !this.iframe){
39825 this.resizeEl.setStyle("overflow", "auto");
39827 // fix randome scrolling
39828 //this.el.on('scroll', function() {
39829 // Roo.log('fix random scolling');
39830 // this.scrollTo('top',0);
39833 content = content || this.content;
39835 this.setContent(content);
39837 if(config && config.url){
39838 this.setUrl(this.url, this.params, this.loadOnce);
39843 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39845 if (this.view && typeof(this.view.xtype) != 'undefined') {
39846 this.view.el = this.el.appendChild(document.createElement("div"));
39847 this.view = Roo.factory(this.view);
39848 this.view.render && this.view.render(false, '');
39852 this.fireEvent('render', this);
39855 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39865 setRegion : function(region){
39866 this.region = region;
39867 this.setActiveClass(region && !this.background);
39871 setActiveClass: function(state)
39874 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39875 this.el.setStyle('position','relative');
39877 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39878 this.el.setStyle('position', 'absolute');
39883 * Returns the toolbar for this Panel if one was configured.
39884 * @return {Roo.Toolbar}
39886 getToolbar : function(){
39887 return this.toolbar;
39890 setActiveState : function(active)
39892 this.active = active;
39893 this.setActiveClass(active);
39895 if(this.fireEvent("deactivate", this) === false){
39900 this.fireEvent("activate", this);
39904 * Updates this panel's element (not for iframe)
39905 * @param {String} content The new content
39906 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39908 setContent : function(content, loadScripts){
39913 this.el.update(content, loadScripts);
39916 ignoreResize : function(w, h){
39917 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39920 this.lastSize = {width: w, height: h};
39925 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39926 * @return {Roo.UpdateManager} The UpdateManager
39928 getUpdateManager : function(){
39932 return this.el.getUpdateManager();
39935 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39936 * Does not work with IFRAME contents
39937 * @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:
39940 url: "your-url.php",
39941 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39942 callback: yourFunction,
39943 scope: yourObject, //(optional scope)
39946 text: "Loading...",
39952 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39953 * 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.
39954 * @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}
39955 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39956 * @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.
39957 * @return {Roo.ContentPanel} this
39965 var um = this.el.getUpdateManager();
39966 um.update.apply(um, arguments);
39972 * 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.
39973 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39974 * @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)
39975 * @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)
39976 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39978 setUrl : function(url, params, loadOnce){
39980 this.iframeEl.dom.src = url;
39984 if(this.refreshDelegate){
39985 this.removeListener("activate", this.refreshDelegate);
39987 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39988 this.on("activate", this.refreshDelegate);
39989 return this.el.getUpdateManager();
39992 _handleRefresh : function(url, params, loadOnce){
39993 if(!loadOnce || !this.loaded){
39994 var updater = this.el.getUpdateManager();
39995 updater.update(url, params, this._setLoaded.createDelegate(this));
39999 _setLoaded : function(){
40000 this.loaded = true;
40004 * Returns this panel's id
40007 getId : function(){
40012 * Returns this panel's element - used by regiosn to add.
40013 * @return {Roo.Element}
40015 getEl : function(){
40016 return this.wrapEl || this.el;
40021 adjustForComponents : function(width, height)
40023 //Roo.log('adjustForComponents ');
40024 if(this.resizeEl != this.el){
40025 width -= this.el.getFrameWidth('lr');
40026 height -= this.el.getFrameWidth('tb');
40029 var te = this.toolbar.getEl();
40030 te.setWidth(width);
40031 height -= te.getHeight();
40034 var te = this.footer.getEl();
40035 te.setWidth(width);
40036 height -= te.getHeight();
40040 if(this.adjustments){
40041 width += this.adjustments[0];
40042 height += this.adjustments[1];
40044 return {"width": width, "height": height};
40047 setSize : function(width, height){
40048 if(this.fitToFrame && !this.ignoreResize(width, height)){
40049 if(this.fitContainer && this.resizeEl != this.el){
40050 this.el.setSize(width, height);
40052 var size = this.adjustForComponents(width, height);
40054 this.iframeEl.setSize(width,height);
40057 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40058 this.fireEvent('resize', this, size.width, size.height);
40065 * Returns this panel's title
40068 getTitle : function(){
40070 if (typeof(this.title) != 'object') {
40075 for (var k in this.title) {
40076 if (!this.title.hasOwnProperty(k)) {
40080 if (k.indexOf('-') >= 0) {
40081 var s = k.split('-');
40082 for (var i = 0; i<s.length; i++) {
40083 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40086 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40093 * Set this panel's title
40094 * @param {String} title
40096 setTitle : function(title){
40097 this.title = title;
40099 this.region.updatePanelTitle(this, title);
40104 * Returns true is this panel was configured to be closable
40105 * @return {Boolean}
40107 isClosable : function(){
40108 return this.closable;
40111 beforeSlide : function(){
40113 this.resizeEl.clip();
40116 afterSlide : function(){
40118 this.resizeEl.unclip();
40122 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40123 * Will fail silently if the {@link #setUrl} method has not been called.
40124 * This does not activate the panel, just updates its content.
40126 refresh : function(){
40127 if(this.refreshDelegate){
40128 this.loaded = false;
40129 this.refreshDelegate();
40134 * Destroys this panel
40136 destroy : function(){
40137 this.el.removeAllListeners();
40138 var tempEl = document.createElement("span");
40139 tempEl.appendChild(this.el.dom);
40140 tempEl.innerHTML = "";
40146 * form - if the content panel contains a form - this is a reference to it.
40147 * @type {Roo.form.Form}
40151 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40152 * This contains a reference to it.
40158 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40168 * @param {Object} cfg Xtype definition of item to add.
40172 getChildContainer: function () {
40173 return this.getEl();
40178 var ret = new Roo.factory(cfg);
40183 if (cfg.xtype.match(/^Form$/)) {
40186 //if (this.footer) {
40187 // el = this.footer.container.insertSibling(false, 'before');
40189 el = this.el.createChild();
40192 this.form = new Roo.form.Form(cfg);
40195 if ( this.form.allItems.length) {
40196 this.form.render(el.dom);
40200 // should only have one of theses..
40201 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40202 // views.. should not be just added - used named prop 'view''
40204 cfg.el = this.el.appendChild(document.createElement("div"));
40207 var ret = new Roo.factory(cfg);
40209 ret.render && ret.render(false, ''); // render blank..
40219 * @class Roo.bootstrap.panel.Grid
40220 * @extends Roo.bootstrap.panel.Content
40222 * Create a new GridPanel.
40223 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40224 * @param {Object} config A the config object
40230 Roo.bootstrap.panel.Grid = function(config)
40234 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40235 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40237 config.el = this.wrapper;
40238 //this.el = this.wrapper;
40240 if (config.container) {
40241 // ctor'ed from a Border/panel.grid
40244 this.wrapper.setStyle("overflow", "hidden");
40245 this.wrapper.addClass('roo-grid-container');
40250 if(config.toolbar){
40251 var tool_el = this.wrapper.createChild();
40252 this.toolbar = Roo.factory(config.toolbar);
40254 if (config.toolbar.items) {
40255 ti = config.toolbar.items ;
40256 delete config.toolbar.items ;
40260 this.toolbar.render(tool_el);
40261 for(var i =0;i < ti.length;i++) {
40262 // Roo.log(['add child', items[i]]);
40263 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40265 this.toolbar.items = nitems;
40267 delete config.toolbar;
40270 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40271 config.grid.scrollBody = true;;
40272 config.grid.monitorWindowResize = false; // turn off autosizing
40273 config.grid.autoHeight = false;
40274 config.grid.autoWidth = false;
40276 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40278 if (config.background) {
40279 // render grid on panel activation (if panel background)
40280 this.on('activate', function(gp) {
40281 if (!gp.grid.rendered) {
40282 gp.grid.render(this.wrapper);
40283 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40288 this.grid.render(this.wrapper);
40289 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40292 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40293 // ??? needed ??? config.el = this.wrapper;
40298 // xtype created footer. - not sure if will work as we normally have to render first..
40299 if (this.footer && !this.footer.el && this.footer.xtype) {
40301 var ctr = this.grid.getView().getFooterPanel(true);
40302 this.footer.dataSource = this.grid.dataSource;
40303 this.footer = Roo.factory(this.footer, Roo);
40304 this.footer.render(ctr);
40314 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40315 getId : function(){
40316 return this.grid.id;
40320 * Returns the grid for this panel
40321 * @return {Roo.bootstrap.Table}
40323 getGrid : function(){
40327 setSize : function(width, height){
40328 if(!this.ignoreResize(width, height)){
40329 var grid = this.grid;
40330 var size = this.adjustForComponents(width, height);
40331 // tfoot is not a footer?
40334 var gridel = grid.getGridEl();
40335 gridel.setSize(size.width, size.height);
40337 var tbd = grid.getGridEl().select('tbody', true).first();
40338 var thd = grid.getGridEl().select('thead',true).first();
40339 var tbf= grid.getGridEl().select('tfoot', true).first();
40342 size.height -= tbf.getHeight();
40345 size.height -= thd.getHeight();
40348 tbd.setSize(size.width, size.height );
40349 // this is for the account management tab -seems to work there.
40350 var thd = grid.getGridEl().select('thead',true).first();
40352 // tbd.setSize(size.width, size.height - thd.getHeight());
40361 beforeSlide : function(){
40362 this.grid.getView().scroller.clip();
40365 afterSlide : function(){
40366 this.grid.getView().scroller.unclip();
40369 destroy : function(){
40370 this.grid.destroy();
40372 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40377 * @class Roo.bootstrap.panel.Nest
40378 * @extends Roo.bootstrap.panel.Content
40380 * Create a new Panel, that can contain a layout.Border.
40383 * @param {Roo.BorderLayout} layout The layout for this panel
40384 * @param {String/Object} config A string to set only the title or a config object
40386 Roo.bootstrap.panel.Nest = function(config)
40388 // construct with only one argument..
40389 /* FIXME - implement nicer consturctors
40390 if (layout.layout) {
40392 layout = config.layout;
40393 delete config.layout;
40395 if (layout.xtype && !layout.getEl) {
40396 // then layout needs constructing..
40397 layout = Roo.factory(layout, Roo);
40401 config.el = config.layout.getEl();
40403 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40405 config.layout.monitorWindowResize = false; // turn off autosizing
40406 this.layout = config.layout;
40407 this.layout.getEl().addClass("roo-layout-nested-layout");
40408 this.layout.parent = this;
40415 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40417 setSize : function(width, height){
40418 if(!this.ignoreResize(width, height)){
40419 var size = this.adjustForComponents(width, height);
40420 var el = this.layout.getEl();
40421 if (size.height < 1) {
40422 el.setWidth(size.width);
40424 el.setSize(size.width, size.height);
40426 var touch = el.dom.offsetWidth;
40427 this.layout.layout();
40428 // ie requires a double layout on the first pass
40429 if(Roo.isIE && !this.initialized){
40430 this.initialized = true;
40431 this.layout.layout();
40436 // activate all subpanels if not currently active..
40438 setActiveState : function(active){
40439 this.active = active;
40440 this.setActiveClass(active);
40443 this.fireEvent("deactivate", this);
40447 this.fireEvent("activate", this);
40448 // not sure if this should happen before or after..
40449 if (!this.layout) {
40450 return; // should not happen..
40453 for (var r in this.layout.regions) {
40454 reg = this.layout.getRegion(r);
40455 if (reg.getActivePanel()) {
40456 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40457 reg.setActivePanel(reg.getActivePanel());
40460 if (!reg.panels.length) {
40463 reg.showPanel(reg.getPanel(0));
40472 * Returns the nested BorderLayout for this panel
40473 * @return {Roo.BorderLayout}
40475 getLayout : function(){
40476 return this.layout;
40480 * Adds a xtype elements to the layout of the nested panel
40484 xtype : 'ContentPanel',
40491 xtype : 'NestedLayoutPanel',
40497 items : [ ... list of content panels or nested layout panels.. ]
40501 * @param {Object} cfg Xtype definition of item to add.
40503 addxtype : function(cfg) {
40504 return this.layout.addxtype(cfg);
40509 * Ext JS Library 1.1.1
40510 * Copyright(c) 2006-2007, Ext JS, LLC.
40512 * Originally Released Under LGPL - original licence link has changed is not relivant.
40515 * <script type="text/javascript">
40518 * @class Roo.TabPanel
40519 * @extends Roo.util.Observable
40520 * A lightweight tab container.
40524 // basic tabs 1, built from existing content
40525 var tabs = new Roo.TabPanel("tabs1");
40526 tabs.addTab("script", "View Script");
40527 tabs.addTab("markup", "View Markup");
40528 tabs.activate("script");
40530 // more advanced tabs, built from javascript
40531 var jtabs = new Roo.TabPanel("jtabs");
40532 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40534 // set up the UpdateManager
40535 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40536 var updater = tab2.getUpdateManager();
40537 updater.setDefaultUrl("ajax1.htm");
40538 tab2.on('activate', updater.refresh, updater, true);
40540 // Use setUrl for Ajax loading
40541 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40542 tab3.setUrl("ajax2.htm", null, true);
40545 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40548 jtabs.activate("jtabs-1");
40551 * Create a new TabPanel.
40552 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40553 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40555 Roo.bootstrap.panel.Tabs = function(config){
40557 * The container element for this TabPanel.
40558 * @type Roo.Element
40560 this.el = Roo.get(config.el);
40563 if(typeof config == "boolean"){
40564 this.tabPosition = config ? "bottom" : "top";
40566 Roo.apply(this, config);
40570 if(this.tabPosition == "bottom"){
40571 // if tabs are at the bottom = create the body first.
40572 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40573 this.el.addClass("roo-tabs-bottom");
40575 // next create the tabs holders
40577 if (this.tabPosition == "west"){
40579 var reg = this.region; // fake it..
40581 if (!reg.mgr.parent) {
40584 reg = reg.mgr.parent.region;
40586 Roo.log("got nest?");
40588 if (reg.mgr.getRegion('west')) {
40589 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40590 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40591 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40592 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40593 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40601 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40602 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40603 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40604 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40609 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40612 // finally - if tabs are at the top, then create the body last..
40613 if(this.tabPosition != "bottom"){
40614 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40615 * @type Roo.Element
40617 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40618 this.el.addClass("roo-tabs-top");
40622 this.bodyEl.setStyle("position", "relative");
40624 this.active = null;
40625 this.activateDelegate = this.activate.createDelegate(this);
40630 * Fires when the active tab changes
40631 * @param {Roo.TabPanel} this
40632 * @param {Roo.TabPanelItem} activePanel The new active tab
40636 * @event beforetabchange
40637 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40638 * @param {Roo.TabPanel} this
40639 * @param {Object} e Set cancel to true on this object to cancel the tab change
40640 * @param {Roo.TabPanelItem} tab The tab being changed to
40642 "beforetabchange" : true
40645 Roo.EventManager.onWindowResize(this.onResize, this);
40646 this.cpad = this.el.getPadding("lr");
40647 this.hiddenCount = 0;
40650 // toolbar on the tabbar support...
40651 if (this.toolbar) {
40652 alert("no toolbar support yet");
40653 this.toolbar = false;
40655 var tcfg = this.toolbar;
40656 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40657 this.toolbar = new Roo.Toolbar(tcfg);
40658 if (Roo.isSafari) {
40659 var tbl = tcfg.container.child('table', true);
40660 tbl.setAttribute('width', '100%');
40668 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40671 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40673 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40675 tabPosition : "top",
40677 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40679 currentTabWidth : 0,
40681 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40685 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40689 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40691 preferredTabWidth : 175,
40693 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40695 resizeTabs : false,
40697 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40699 monitorResize : true,
40701 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40703 toolbar : false, // set by caller..
40705 region : false, /// set by caller
40707 disableTooltips : true, // not used yet...
40710 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40711 * @param {String} id The id of the div to use <b>or create</b>
40712 * @param {String} text The text for the tab
40713 * @param {String} content (optional) Content to put in the TabPanelItem body
40714 * @param {Boolean} closable (optional) True to create a close icon on the tab
40715 * @return {Roo.TabPanelItem} The created TabPanelItem
40717 addTab : function(id, text, content, closable, tpl)
40719 var item = new Roo.bootstrap.panel.TabItem({
40723 closable : closable,
40726 this.addTabItem(item);
40728 item.setContent(content);
40734 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40735 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40736 * @return {Roo.TabPanelItem}
40738 getTab : function(id){
40739 return this.items[id];
40743 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40744 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40746 hideTab : function(id){
40747 var t = this.items[id];
40750 this.hiddenCount++;
40751 this.autoSizeTabs();
40756 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40757 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40759 unhideTab : function(id){
40760 var t = this.items[id];
40762 t.setHidden(false);
40763 this.hiddenCount--;
40764 this.autoSizeTabs();
40769 * Adds an existing {@link Roo.TabPanelItem}.
40770 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40772 addTabItem : function(item)
40774 this.items[item.id] = item;
40775 this.items.push(item);
40776 this.autoSizeTabs();
40777 // if(this.resizeTabs){
40778 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40779 // this.autoSizeTabs();
40781 // item.autoSize();
40786 * Removes a {@link Roo.TabPanelItem}.
40787 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40789 removeTab : function(id){
40790 var items = this.items;
40791 var tab = items[id];
40792 if(!tab) { return; }
40793 var index = items.indexOf(tab);
40794 if(this.active == tab && items.length > 1){
40795 var newTab = this.getNextAvailable(index);
40800 this.stripEl.dom.removeChild(tab.pnode.dom);
40801 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40802 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40804 items.splice(index, 1);
40805 delete this.items[tab.id];
40806 tab.fireEvent("close", tab);
40807 tab.purgeListeners();
40808 this.autoSizeTabs();
40811 getNextAvailable : function(start){
40812 var items = this.items;
40814 // look for a next tab that will slide over to
40815 // replace the one being removed
40816 while(index < items.length){
40817 var item = items[++index];
40818 if(item && !item.isHidden()){
40822 // if one isn't found select the previous tab (on the left)
40825 var item = items[--index];
40826 if(item && !item.isHidden()){
40834 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40835 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40837 disableTab : function(id){
40838 var tab = this.items[id];
40839 if(tab && this.active != tab){
40845 * Enables a {@link Roo.TabPanelItem} that is disabled.
40846 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40848 enableTab : function(id){
40849 var tab = this.items[id];
40854 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40855 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40856 * @return {Roo.TabPanelItem} The TabPanelItem.
40858 activate : function(id)
40860 //Roo.log('activite:' + id);
40862 var tab = this.items[id];
40866 if(tab == this.active || tab.disabled){
40870 this.fireEvent("beforetabchange", this, e, tab);
40871 if(e.cancel !== true && !tab.disabled){
40873 this.active.hide();
40875 this.active = this.items[id];
40876 this.active.show();
40877 this.fireEvent("tabchange", this, this.active);
40883 * Gets the active {@link Roo.TabPanelItem}.
40884 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40886 getActiveTab : function(){
40887 return this.active;
40891 * Updates the tab body element to fit the height of the container element
40892 * for overflow scrolling
40893 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40895 syncHeight : function(targetHeight){
40896 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40897 var bm = this.bodyEl.getMargins();
40898 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40899 this.bodyEl.setHeight(newHeight);
40903 onResize : function(){
40904 if(this.monitorResize){
40905 this.autoSizeTabs();
40910 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40912 beginUpdate : function(){
40913 this.updating = true;
40917 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40919 endUpdate : function(){
40920 this.updating = false;
40921 this.autoSizeTabs();
40925 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40927 autoSizeTabs : function()
40929 var count = this.items.length;
40930 var vcount = count - this.hiddenCount;
40933 this.stripEl.hide();
40935 this.stripEl.show();
40938 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40943 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40944 var availWidth = Math.floor(w / vcount);
40945 var b = this.stripBody;
40946 if(b.getWidth() > w){
40947 var tabs = this.items;
40948 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40949 if(availWidth < this.minTabWidth){
40950 /*if(!this.sleft){ // incomplete scrolling code
40951 this.createScrollButtons();
40954 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40957 if(this.currentTabWidth < this.preferredTabWidth){
40958 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40964 * Returns the number of tabs in this TabPanel.
40967 getCount : function(){
40968 return this.items.length;
40972 * Resizes all the tabs to the passed width
40973 * @param {Number} The new width
40975 setTabWidth : function(width){
40976 this.currentTabWidth = width;
40977 for(var i = 0, len = this.items.length; i < len; i++) {
40978 if(!this.items[i].isHidden()) {
40979 this.items[i].setWidth(width);
40985 * Destroys this TabPanel
40986 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40988 destroy : function(removeEl){
40989 Roo.EventManager.removeResizeListener(this.onResize, this);
40990 for(var i = 0, len = this.items.length; i < len; i++){
40991 this.items[i].purgeListeners();
40993 if(removeEl === true){
40994 this.el.update("");
40999 createStrip : function(container)
41001 var strip = document.createElement("nav");
41002 strip.className = Roo.bootstrap.version == 4 ?
41003 "navbar-light bg-light" :
41004 "navbar navbar-default"; //"x-tabs-wrap";
41005 container.appendChild(strip);
41009 createStripList : function(strip)
41011 // div wrapper for retard IE
41012 // returns the "tr" element.
41013 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41014 //'<div class="x-tabs-strip-wrap">'+
41015 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41016 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41017 return strip.firstChild; //.firstChild.firstChild.firstChild;
41019 createBody : function(container)
41021 var body = document.createElement("div");
41022 Roo.id(body, "tab-body");
41023 //Roo.fly(body).addClass("x-tabs-body");
41024 Roo.fly(body).addClass("tab-content");
41025 container.appendChild(body);
41028 createItemBody :function(bodyEl, id){
41029 var body = Roo.getDom(id);
41031 body = document.createElement("div");
41034 //Roo.fly(body).addClass("x-tabs-item-body");
41035 Roo.fly(body).addClass("tab-pane");
41036 bodyEl.insertBefore(body, bodyEl.firstChild);
41040 createStripElements : function(stripEl, text, closable, tpl)
41042 var td = document.createElement("li"); // was td..
41043 td.className = 'nav-item';
41045 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41048 stripEl.appendChild(td);
41050 td.className = "x-tabs-closable";
41051 if(!this.closeTpl){
41052 this.closeTpl = new Roo.Template(
41053 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41054 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41055 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41058 var el = this.closeTpl.overwrite(td, {"text": text});
41059 var close = el.getElementsByTagName("div")[0];
41060 var inner = el.getElementsByTagName("em")[0];
41061 return {"el": el, "close": close, "inner": inner};
41064 // not sure what this is..
41065 // if(!this.tabTpl){
41066 //this.tabTpl = new Roo.Template(
41067 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41068 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41070 // this.tabTpl = new Roo.Template(
41071 // '<a href="#">' +
41072 // '<span unselectable="on"' +
41073 // (this.disableTooltips ? '' : ' title="{text}"') +
41074 // ' >{text}</span></a>'
41080 var template = tpl || this.tabTpl || false;
41083 template = new Roo.Template(
41084 Roo.bootstrap.version == 4 ?
41086 '<a class="nav-link" href="#" unselectable="on"' +
41087 (this.disableTooltips ? '' : ' title="{text}"') +
41090 '<a class="nav-link" href="#">' +
41091 '<span unselectable="on"' +
41092 (this.disableTooltips ? '' : ' title="{text}"') +
41093 ' >{text}</span></a>'
41098 switch (typeof(template)) {
41102 template = new Roo.Template(template);
41108 var el = template.overwrite(td, {"text": text});
41110 var inner = el.getElementsByTagName("span")[0];
41112 return {"el": el, "inner": inner};
41120 * @class Roo.TabPanelItem
41121 * @extends Roo.util.Observable
41122 * Represents an individual item (tab plus body) in a TabPanel.
41123 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41124 * @param {String} id The id of this TabPanelItem
41125 * @param {String} text The text for the tab of this TabPanelItem
41126 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41128 Roo.bootstrap.panel.TabItem = function(config){
41130 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41131 * @type Roo.TabPanel
41133 this.tabPanel = config.panel;
41135 * The id for this TabPanelItem
41138 this.id = config.id;
41140 this.disabled = false;
41142 this.text = config.text;
41144 this.loaded = false;
41145 this.closable = config.closable;
41148 * The body element for this TabPanelItem.
41149 * @type Roo.Element
41151 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41152 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41153 this.bodyEl.setStyle("display", "block");
41154 this.bodyEl.setStyle("zoom", "1");
41155 //this.hideAction();
41157 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41159 this.el = Roo.get(els.el);
41160 this.inner = Roo.get(els.inner, true);
41161 this.textEl = Roo.bootstrap.version == 4 ?
41162 this.el : Roo.get(this.el.dom.firstChild, true);
41164 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41165 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41168 // this.el.on("mousedown", this.onTabMouseDown, this);
41169 this.el.on("click", this.onTabClick, this);
41171 if(config.closable){
41172 var c = Roo.get(els.close, true);
41173 c.dom.title = this.closeText;
41174 c.addClassOnOver("close-over");
41175 c.on("click", this.closeClick, this);
41181 * Fires when this tab becomes the active tab.
41182 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41183 * @param {Roo.TabPanelItem} this
41187 * @event beforeclose
41188 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41189 * @param {Roo.TabPanelItem} this
41190 * @param {Object} e Set cancel to true on this object to cancel the close.
41192 "beforeclose": true,
41195 * Fires when this tab is closed.
41196 * @param {Roo.TabPanelItem} this
41200 * @event deactivate
41201 * Fires when this tab is no longer the active tab.
41202 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41203 * @param {Roo.TabPanelItem} this
41205 "deactivate" : true
41207 this.hidden = false;
41209 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41212 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41214 purgeListeners : function(){
41215 Roo.util.Observable.prototype.purgeListeners.call(this);
41216 this.el.removeAllListeners();
41219 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41222 this.status_node.addClass("active");
41225 this.tabPanel.stripWrap.repaint();
41227 this.fireEvent("activate", this.tabPanel, this);
41231 * Returns true if this tab is the active tab.
41232 * @return {Boolean}
41234 isActive : function(){
41235 return this.tabPanel.getActiveTab() == this;
41239 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41242 this.status_node.removeClass("active");
41244 this.fireEvent("deactivate", this.tabPanel, this);
41247 hideAction : function(){
41248 this.bodyEl.hide();
41249 this.bodyEl.setStyle("position", "absolute");
41250 this.bodyEl.setLeft("-20000px");
41251 this.bodyEl.setTop("-20000px");
41254 showAction : function(){
41255 this.bodyEl.setStyle("position", "relative");
41256 this.bodyEl.setTop("");
41257 this.bodyEl.setLeft("");
41258 this.bodyEl.show();
41262 * Set the tooltip for the tab.
41263 * @param {String} tooltip The tab's tooltip
41265 setTooltip : function(text){
41266 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41267 this.textEl.dom.qtip = text;
41268 this.textEl.dom.removeAttribute('title');
41270 this.textEl.dom.title = text;
41274 onTabClick : function(e){
41275 e.preventDefault();
41276 this.tabPanel.activate(this.id);
41279 onTabMouseDown : function(e){
41280 e.preventDefault();
41281 this.tabPanel.activate(this.id);
41284 getWidth : function(){
41285 return this.inner.getWidth();
41288 setWidth : function(width){
41289 var iwidth = width - this.linode.getPadding("lr");
41290 this.inner.setWidth(iwidth);
41291 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41292 this.linode.setWidth(width);
41296 * Show or hide the tab
41297 * @param {Boolean} hidden True to hide or false to show.
41299 setHidden : function(hidden){
41300 this.hidden = hidden;
41301 this.linode.setStyle("display", hidden ? "none" : "");
41305 * Returns true if this tab is "hidden"
41306 * @return {Boolean}
41308 isHidden : function(){
41309 return this.hidden;
41313 * Returns the text for this tab
41316 getText : function(){
41320 autoSize : function(){
41321 //this.el.beginMeasure();
41322 this.textEl.setWidth(1);
41324 * #2804 [new] Tabs in Roojs
41325 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41327 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41328 //this.el.endMeasure();
41332 * Sets the text for the tab (Note: this also sets the tooltip text)
41333 * @param {String} text The tab's text and tooltip
41335 setText : function(text){
41337 this.textEl.update(text);
41338 this.setTooltip(text);
41339 //if(!this.tabPanel.resizeTabs){
41340 // this.autoSize();
41344 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41346 activate : function(){
41347 this.tabPanel.activate(this.id);
41351 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41353 disable : function(){
41354 if(this.tabPanel.active != this){
41355 this.disabled = true;
41356 this.status_node.addClass("disabled");
41361 * Enables this TabPanelItem if it was previously disabled.
41363 enable : function(){
41364 this.disabled = false;
41365 this.status_node.removeClass("disabled");
41369 * Sets the content for this TabPanelItem.
41370 * @param {String} content The content
41371 * @param {Boolean} loadScripts true to look for and load scripts
41373 setContent : function(content, loadScripts){
41374 this.bodyEl.update(content, loadScripts);
41378 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41379 * @return {Roo.UpdateManager} The UpdateManager
41381 getUpdateManager : function(){
41382 return this.bodyEl.getUpdateManager();
41386 * Set a URL to be used to load the content for this TabPanelItem.
41387 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41388 * @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)
41389 * @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)
41390 * @return {Roo.UpdateManager} The UpdateManager
41392 setUrl : function(url, params, loadOnce){
41393 if(this.refreshDelegate){
41394 this.un('activate', this.refreshDelegate);
41396 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41397 this.on("activate", this.refreshDelegate);
41398 return this.bodyEl.getUpdateManager();
41402 _handleRefresh : function(url, params, loadOnce){
41403 if(!loadOnce || !this.loaded){
41404 var updater = this.bodyEl.getUpdateManager();
41405 updater.update(url, params, this._setLoaded.createDelegate(this));
41410 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41411 * Will fail silently if the setUrl method has not been called.
41412 * This does not activate the panel, just updates its content.
41414 refresh : function(){
41415 if(this.refreshDelegate){
41416 this.loaded = false;
41417 this.refreshDelegate();
41422 _setLoaded : function(){
41423 this.loaded = true;
41427 closeClick : function(e){
41430 this.fireEvent("beforeclose", this, o);
41431 if(o.cancel !== true){
41432 this.tabPanel.removeTab(this.id);
41436 * The text displayed in the tooltip for the close icon.
41439 closeText : "Close this tab"
41442 * This script refer to:
41443 * Title: International Telephone Input
41444 * Author: Jack O'Connor
41445 * Code version: v12.1.12
41446 * Availability: https://github.com/jackocnr/intl-tel-input.git
41449 Roo.bootstrap.PhoneInputData = function() {
41452 "Afghanistan (افغانستان)",
41457 "Albania (Shqipëri)",
41462 "Algeria (الجزائر)",
41487 "Antigua and Barbuda",
41497 "Armenia (Հայաստան)",
41513 "Austria (Österreich)",
41518 "Azerbaijan (Azərbaycan)",
41528 "Bahrain (البحرين)",
41533 "Bangladesh (বাংলাদেশ)",
41543 "Belarus (Беларусь)",
41548 "Belgium (België)",
41578 "Bosnia and Herzegovina (Босна и Херцеговина)",
41593 "British Indian Ocean Territory",
41598 "British Virgin Islands",
41608 "Bulgaria (България)",
41618 "Burundi (Uburundi)",
41623 "Cambodia (កម្ពុជា)",
41628 "Cameroon (Cameroun)",
41637 ["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"]
41640 "Cape Verde (Kabu Verdi)",
41645 "Caribbean Netherlands",
41656 "Central African Republic (République centrafricaine)",
41676 "Christmas Island",
41682 "Cocos (Keeling) Islands",
41693 "Comoros (جزر القمر)",
41698 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41703 "Congo (Republic) (Congo-Brazzaville)",
41723 "Croatia (Hrvatska)",
41744 "Czech Republic (Česká republika)",
41749 "Denmark (Danmark)",
41764 "Dominican Republic (República Dominicana)",
41768 ["809", "829", "849"]
41786 "Equatorial Guinea (Guinea Ecuatorial)",
41806 "Falkland Islands (Islas Malvinas)",
41811 "Faroe Islands (Føroyar)",
41832 "French Guiana (Guyane française)",
41837 "French Polynesia (Polynésie française)",
41852 "Georgia (საქართველო)",
41857 "Germany (Deutschland)",
41877 "Greenland (Kalaallit Nunaat)",
41914 "Guinea-Bissau (Guiné Bissau)",
41939 "Hungary (Magyarország)",
41944 "Iceland (Ísland)",
41964 "Iraq (العراق)",
41980 "Israel (ישראל)",
42007 "Jordan (الأردن)",
42012 "Kazakhstan (Казахстан)",
42033 "Kuwait (الكويت)",
42038 "Kyrgyzstan (Кыргызстан)",
42048 "Latvia (Latvija)",
42053 "Lebanon (لبنان)",
42068 "Libya (ليبيا)",
42078 "Lithuania (Lietuva)",
42093 "Macedonia (FYROM) (Македонија)",
42098 "Madagascar (Madagasikara)",
42128 "Marshall Islands",
42138 "Mauritania (موريتانيا)",
42143 "Mauritius (Moris)",
42164 "Moldova (Republica Moldova)",
42174 "Mongolia (Монгол)",
42179 "Montenegro (Crna Gora)",
42189 "Morocco (المغرب)",
42195 "Mozambique (Moçambique)",
42200 "Myanmar (Burma) (မြန်မာ)",
42205 "Namibia (Namibië)",
42220 "Netherlands (Nederland)",
42225 "New Caledonia (Nouvelle-Calédonie)",
42260 "North Korea (조선 민주주의 인민 공화국)",
42265 "Northern Mariana Islands",
42281 "Pakistan (پاکستان)",
42291 "Palestine (فلسطين)",
42301 "Papua New Guinea",
42343 "Réunion (La Réunion)",
42349 "Romania (România)",
42365 "Saint Barthélemy",
42376 "Saint Kitts and Nevis",
42386 "Saint Martin (Saint-Martin (partie française))",
42392 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42397 "Saint Vincent and the Grenadines",
42412 "São Tomé and Príncipe (São Tomé e Príncipe)",
42417 "Saudi Arabia (المملكة العربية السعودية)",
42422 "Senegal (Sénégal)",
42452 "Slovakia (Slovensko)",
42457 "Slovenia (Slovenija)",
42467 "Somalia (Soomaaliya)",
42477 "South Korea (대한민국)",
42482 "South Sudan (جنوب السودان)",
42492 "Sri Lanka (ශ්රී ලංකාව)",
42497 "Sudan (السودان)",
42507 "Svalbard and Jan Mayen",
42518 "Sweden (Sverige)",
42523 "Switzerland (Schweiz)",
42528 "Syria (سوريا)",
42573 "Trinidad and Tobago",
42578 "Tunisia (تونس)",
42583 "Turkey (Türkiye)",
42593 "Turks and Caicos Islands",
42603 "U.S. Virgin Islands",
42613 "Ukraine (Україна)",
42618 "United Arab Emirates (الإمارات العربية المتحدة)",
42640 "Uzbekistan (Oʻzbekiston)",
42650 "Vatican City (Città del Vaticano)",
42661 "Vietnam (Việt Nam)",
42666 "Wallis and Futuna (Wallis-et-Futuna)",
42671 "Western Sahara (الصحراء الغربية)",
42677 "Yemen (اليمن)",
42701 * This script refer to:
42702 * Title: International Telephone Input
42703 * Author: Jack O'Connor
42704 * Code version: v12.1.12
42705 * Availability: https://github.com/jackocnr/intl-tel-input.git
42709 * @class Roo.bootstrap.PhoneInput
42710 * @extends Roo.bootstrap.TriggerField
42711 * An input with International dial-code selection
42713 * @cfg {String} defaultDialCode default '+852'
42714 * @cfg {Array} preferedCountries default []
42717 * Create a new PhoneInput.
42718 * @param {Object} config Configuration options
42721 Roo.bootstrap.PhoneInput = function(config) {
42722 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42725 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42727 listWidth: undefined,
42729 selectedClass: 'active',
42731 invalidClass : "has-warning",
42733 validClass: 'has-success',
42735 allowed: '0123456789',
42740 * @cfg {String} defaultDialCode The default dial code when initializing the input
42742 defaultDialCode: '+852',
42745 * @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
42747 preferedCountries: false,
42749 getAutoCreate : function()
42751 var data = Roo.bootstrap.PhoneInputData();
42752 var align = this.labelAlign || this.parentLabelAlign();
42755 this.allCountries = [];
42756 this.dialCodeMapping = [];
42758 for (var i = 0; i < data.length; i++) {
42760 this.allCountries[i] = {
42764 priority: c[3] || 0,
42765 areaCodes: c[4] || null
42767 this.dialCodeMapping[c[2]] = {
42770 priority: c[3] || 0,
42771 areaCodes: c[4] || null
42783 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42784 maxlength: this.max_length,
42785 cls : 'form-control tel-input',
42786 autocomplete: 'new-password'
42789 var hiddenInput = {
42792 cls: 'hidden-tel-input'
42796 hiddenInput.name = this.name;
42799 if (this.disabled) {
42800 input.disabled = true;
42803 var flag_container = {
42820 cls: this.hasFeedback ? 'has-feedback' : '',
42826 cls: 'dial-code-holder',
42833 cls: 'roo-select2-container input-group',
42840 if (this.fieldLabel.length) {
42843 tooltip: 'This field is required'
42849 cls: 'control-label',
42855 html: this.fieldLabel
42858 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42864 if(this.indicatorpos == 'right') {
42865 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42872 if(align == 'left') {
42880 if(this.labelWidth > 12){
42881 label.style = "width: " + this.labelWidth + 'px';
42883 if(this.labelWidth < 13 && this.labelmd == 0){
42884 this.labelmd = this.labelWidth;
42886 if(this.labellg > 0){
42887 label.cls += ' col-lg-' + this.labellg;
42888 input.cls += ' col-lg-' + (12 - this.labellg);
42890 if(this.labelmd > 0){
42891 label.cls += ' col-md-' + this.labelmd;
42892 container.cls += ' col-md-' + (12 - this.labelmd);
42894 if(this.labelsm > 0){
42895 label.cls += ' col-sm-' + this.labelsm;
42896 container.cls += ' col-sm-' + (12 - this.labelsm);
42898 if(this.labelxs > 0){
42899 label.cls += ' col-xs-' + this.labelxs;
42900 container.cls += ' col-xs-' + (12 - this.labelxs);
42910 var settings = this;
42912 ['xs','sm','md','lg'].map(function(size){
42913 if (settings[size]) {
42914 cfg.cls += ' col-' + size + '-' + settings[size];
42918 this.store = new Roo.data.Store({
42919 proxy : new Roo.data.MemoryProxy({}),
42920 reader : new Roo.data.JsonReader({
42931 'name' : 'dialCode',
42935 'name' : 'priority',
42939 'name' : 'areaCodes',
42946 if(!this.preferedCountries) {
42947 this.preferedCountries = [
42954 var p = this.preferedCountries.reverse();
42957 for (var i = 0; i < p.length; i++) {
42958 for (var j = 0; j < this.allCountries.length; j++) {
42959 if(this.allCountries[j].iso2 == p[i]) {
42960 var t = this.allCountries[j];
42961 this.allCountries.splice(j,1);
42962 this.allCountries.unshift(t);
42968 this.store.proxy.data = {
42970 data: this.allCountries
42976 initEvents : function()
42979 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42981 this.indicator = this.indicatorEl();
42982 this.flag = this.flagEl();
42983 this.dialCodeHolder = this.dialCodeHolderEl();
42985 this.trigger = this.el.select('div.flag-box',true).first();
42986 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42991 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42992 _this.list.setWidth(lw);
42995 this.list.on('mouseover', this.onViewOver, this);
42996 this.list.on('mousemove', this.onViewMove, this);
42997 this.inputEl().on("keyup", this.onKeyUp, this);
42998 this.inputEl().on("keypress", this.onKeyPress, this);
43000 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43002 this.view = new Roo.View(this.list, this.tpl, {
43003 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43006 this.view.on('click', this.onViewClick, this);
43007 this.setValue(this.defaultDialCode);
43010 onTriggerClick : function(e)
43012 Roo.log('trigger click');
43017 if(this.isExpanded()){
43019 this.hasFocus = false;
43021 this.store.load({});
43022 this.hasFocus = true;
43027 isExpanded : function()
43029 return this.list.isVisible();
43032 collapse : function()
43034 if(!this.isExpanded()){
43038 Roo.get(document).un('mousedown', this.collapseIf, this);
43039 Roo.get(document).un('mousewheel', this.collapseIf, this);
43040 this.fireEvent('collapse', this);
43044 expand : function()
43048 if(this.isExpanded() || !this.hasFocus){
43052 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43053 this.list.setWidth(lw);
43056 this.restrictHeight();
43058 Roo.get(document).on('mousedown', this.collapseIf, this);
43059 Roo.get(document).on('mousewheel', this.collapseIf, this);
43061 this.fireEvent('expand', this);
43064 restrictHeight : function()
43066 this.list.alignTo(this.inputEl(), this.listAlign);
43067 this.list.alignTo(this.inputEl(), this.listAlign);
43070 onViewOver : function(e, t)
43072 if(this.inKeyMode){
43075 var item = this.view.findItemFromChild(t);
43078 var index = this.view.indexOf(item);
43079 this.select(index, false);
43084 onViewClick : function(view, doFocus, el, e)
43086 var index = this.view.getSelectedIndexes()[0];
43088 var r = this.store.getAt(index);
43091 this.onSelect(r, index);
43093 if(doFocus !== false && !this.blockFocus){
43094 this.inputEl().focus();
43098 onViewMove : function(e, t)
43100 this.inKeyMode = false;
43103 select : function(index, scrollIntoView)
43105 this.selectedIndex = index;
43106 this.view.select(index);
43107 if(scrollIntoView !== false){
43108 var el = this.view.getNode(index);
43110 this.list.scrollChildIntoView(el, false);
43115 createList : function()
43117 this.list = Roo.get(document.body).createChild({
43119 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43120 style: 'display:none'
43123 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43126 collapseIf : function(e)
43128 var in_combo = e.within(this.el);
43129 var in_list = e.within(this.list);
43130 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43132 if (in_combo || in_list || is_list) {
43138 onSelect : function(record, index)
43140 if(this.fireEvent('beforeselect', this, record, index) !== false){
43142 this.setFlagClass(record.data.iso2);
43143 this.setDialCode(record.data.dialCode);
43144 this.hasFocus = false;
43146 this.fireEvent('select', this, record, index);
43150 flagEl : function()
43152 var flag = this.el.select('div.flag',true).first();
43159 dialCodeHolderEl : function()
43161 var d = this.el.select('input.dial-code-holder',true).first();
43168 setDialCode : function(v)
43170 this.dialCodeHolder.dom.value = '+'+v;
43173 setFlagClass : function(n)
43175 this.flag.dom.className = 'flag '+n;
43178 getValue : function()
43180 var v = this.inputEl().getValue();
43181 if(this.dialCodeHolder) {
43182 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43187 setValue : function(v)
43189 var d = this.getDialCode(v);
43191 //invalid dial code
43192 if(v.length == 0 || !d || d.length == 0) {
43194 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43195 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43201 this.setFlagClass(this.dialCodeMapping[d].iso2);
43202 this.setDialCode(d);
43203 this.inputEl().dom.value = v.replace('+'+d,'');
43204 this.hiddenEl().dom.value = this.getValue();
43209 getDialCode : function(v)
43213 if (v.length == 0) {
43214 return this.dialCodeHolder.dom.value;
43218 if (v.charAt(0) != "+") {
43221 var numericChars = "";
43222 for (var i = 1; i < v.length; i++) {
43223 var c = v.charAt(i);
43226 if (this.dialCodeMapping[numericChars]) {
43227 dialCode = v.substr(1, i);
43229 if (numericChars.length == 4) {
43239 this.setValue(this.defaultDialCode);
43243 hiddenEl : function()
43245 return this.el.select('input.hidden-tel-input',true).first();
43248 // after setting val
43249 onKeyUp : function(e){
43250 this.setValue(this.getValue());
43253 onKeyPress : function(e){
43254 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43261 * @class Roo.bootstrap.MoneyField
43262 * @extends Roo.bootstrap.ComboBox
43263 * Bootstrap MoneyField class
43266 * Create a new MoneyField.
43267 * @param {Object} config Configuration options
43270 Roo.bootstrap.MoneyField = function(config) {
43272 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43276 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43279 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43281 allowDecimals : true,
43283 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43285 decimalSeparator : ".",
43287 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43289 decimalPrecision : 0,
43291 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43293 allowNegative : true,
43295 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43299 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43301 minValue : Number.NEGATIVE_INFINITY,
43303 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43305 maxValue : Number.MAX_VALUE,
43307 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43309 minText : "The minimum value for this field is {0}",
43311 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43313 maxText : "The maximum value for this field is {0}",
43315 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43316 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43318 nanText : "{0} is not a valid number",
43320 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43324 * @cfg {String} defaults currency of the MoneyField
43325 * value should be in lkey
43327 defaultCurrency : false,
43329 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43331 thousandsDelimiter : false,
43333 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43344 getAutoCreate : function()
43346 var align = this.labelAlign || this.parentLabelAlign();
43358 cls : 'form-control roo-money-amount-input',
43359 autocomplete: 'new-password'
43362 var hiddenInput = {
43366 cls: 'hidden-number-input'
43369 if(this.max_length) {
43370 input.maxlength = this.max_length;
43374 hiddenInput.name = this.name;
43377 if (this.disabled) {
43378 input.disabled = true;
43381 var clg = 12 - this.inputlg;
43382 var cmd = 12 - this.inputmd;
43383 var csm = 12 - this.inputsm;
43384 var cxs = 12 - this.inputxs;
43388 cls : 'row roo-money-field',
43392 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43396 cls: 'roo-select2-container input-group',
43400 cls : 'form-control roo-money-currency-input',
43401 autocomplete: 'new-password',
43403 name : this.currencyName
43407 cls : 'input-group-addon',
43421 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43425 cls: this.hasFeedback ? 'has-feedback' : '',
43436 if (this.fieldLabel.length) {
43439 tooltip: 'This field is required'
43445 cls: 'control-label',
43451 html: this.fieldLabel
43454 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43460 if(this.indicatorpos == 'right') {
43461 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43468 if(align == 'left') {
43476 if(this.labelWidth > 12){
43477 label.style = "width: " + this.labelWidth + 'px';
43479 if(this.labelWidth < 13 && this.labelmd == 0){
43480 this.labelmd = this.labelWidth;
43482 if(this.labellg > 0){
43483 label.cls += ' col-lg-' + this.labellg;
43484 input.cls += ' col-lg-' + (12 - this.labellg);
43486 if(this.labelmd > 0){
43487 label.cls += ' col-md-' + this.labelmd;
43488 container.cls += ' col-md-' + (12 - this.labelmd);
43490 if(this.labelsm > 0){
43491 label.cls += ' col-sm-' + this.labelsm;
43492 container.cls += ' col-sm-' + (12 - this.labelsm);
43494 if(this.labelxs > 0){
43495 label.cls += ' col-xs-' + this.labelxs;
43496 container.cls += ' col-xs-' + (12 - this.labelxs);
43507 var settings = this;
43509 ['xs','sm','md','lg'].map(function(size){
43510 if (settings[size]) {
43511 cfg.cls += ' col-' + size + '-' + settings[size];
43518 initEvents : function()
43520 this.indicator = this.indicatorEl();
43522 this.initCurrencyEvent();
43524 this.initNumberEvent();
43527 initCurrencyEvent : function()
43530 throw "can not find store for combo";
43533 this.store = Roo.factory(this.store, Roo.data);
43534 this.store.parent = this;
43538 this.triggerEl = this.el.select('.input-group-addon', true).first();
43540 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43545 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43546 _this.list.setWidth(lw);
43549 this.list.on('mouseover', this.onViewOver, this);
43550 this.list.on('mousemove', this.onViewMove, this);
43551 this.list.on('scroll', this.onViewScroll, this);
43554 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43557 this.view = new Roo.View(this.list, this.tpl, {
43558 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43561 this.view.on('click', this.onViewClick, this);
43563 this.store.on('beforeload', this.onBeforeLoad, this);
43564 this.store.on('load', this.onLoad, this);
43565 this.store.on('loadexception', this.onLoadException, this);
43567 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43568 "up" : function(e){
43569 this.inKeyMode = true;
43573 "down" : function(e){
43574 if(!this.isExpanded()){
43575 this.onTriggerClick();
43577 this.inKeyMode = true;
43582 "enter" : function(e){
43585 if(this.fireEvent("specialkey", this, e)){
43586 this.onViewClick(false);
43592 "esc" : function(e){
43596 "tab" : function(e){
43599 if(this.fireEvent("specialkey", this, e)){
43600 this.onViewClick(false);
43608 doRelay : function(foo, bar, hname){
43609 if(hname == 'down' || this.scope.isExpanded()){
43610 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43618 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43622 initNumberEvent : function(e)
43624 this.inputEl().on("keydown" , this.fireKey, this);
43625 this.inputEl().on("focus", this.onFocus, this);
43626 this.inputEl().on("blur", this.onBlur, this);
43628 this.inputEl().relayEvent('keyup', this);
43630 if(this.indicator){
43631 this.indicator.addClass('invisible');
43634 this.originalValue = this.getValue();
43636 if(this.validationEvent == 'keyup'){
43637 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43638 this.inputEl().on('keyup', this.filterValidation, this);
43640 else if(this.validationEvent !== false){
43641 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43644 if(this.selectOnFocus){
43645 this.on("focus", this.preFocus, this);
43648 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43649 this.inputEl().on("keypress", this.filterKeys, this);
43651 this.inputEl().relayEvent('keypress', this);
43654 var allowed = "0123456789";
43656 if(this.allowDecimals){
43657 allowed += this.decimalSeparator;
43660 if(this.allowNegative){
43664 if(this.thousandsDelimiter) {
43668 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43670 var keyPress = function(e){
43672 var k = e.getKey();
43674 var c = e.getCharCode();
43677 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43678 allowed.indexOf(String.fromCharCode(c)) === -1
43684 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43688 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43693 this.inputEl().on("keypress", keyPress, this);
43697 onTriggerClick : function(e)
43704 this.loadNext = false;
43706 if(this.isExpanded()){
43711 this.hasFocus = true;
43713 if(this.triggerAction == 'all') {
43714 this.doQuery(this.allQuery, true);
43718 this.doQuery(this.getRawValue());
43721 getCurrency : function()
43723 var v = this.currencyEl().getValue();
43728 restrictHeight : function()
43730 this.list.alignTo(this.currencyEl(), this.listAlign);
43731 this.list.alignTo(this.currencyEl(), this.listAlign);
43734 onViewClick : function(view, doFocus, el, e)
43736 var index = this.view.getSelectedIndexes()[0];
43738 var r = this.store.getAt(index);
43741 this.onSelect(r, index);
43745 onSelect : function(record, index){
43747 if(this.fireEvent('beforeselect', this, record, index) !== false){
43749 this.setFromCurrencyData(index > -1 ? record.data : false);
43753 this.fireEvent('select', this, record, index);
43757 setFromCurrencyData : function(o)
43761 this.lastCurrency = o;
43763 if (this.currencyField) {
43764 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43766 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43769 this.lastSelectionText = currency;
43771 //setting default currency
43772 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43773 this.setCurrency(this.defaultCurrency);
43777 this.setCurrency(currency);
43780 setFromData : function(o)
43784 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43786 this.setFromCurrencyData(c);
43791 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43793 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43796 this.setValue(value);
43800 setCurrency : function(v)
43802 this.currencyValue = v;
43805 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43810 setValue : function(v)
43812 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43818 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43820 this.inputEl().dom.value = (v == '') ? '' :
43821 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43823 if(!this.allowZero && v === '0') {
43824 this.hiddenEl().dom.value = '';
43825 this.inputEl().dom.value = '';
43832 getRawValue : function()
43834 var v = this.inputEl().getValue();
43839 getValue : function()
43841 return this.fixPrecision(this.parseValue(this.getRawValue()));
43844 parseValue : function(value)
43846 if(this.thousandsDelimiter) {
43848 r = new RegExp(",", "g");
43849 value = value.replace(r, "");
43852 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43853 return isNaN(value) ? '' : value;
43857 fixPrecision : function(value)
43859 if(this.thousandsDelimiter) {
43861 r = new RegExp(",", "g");
43862 value = value.replace(r, "");
43865 var nan = isNaN(value);
43867 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43868 return nan ? '' : value;
43870 return parseFloat(value).toFixed(this.decimalPrecision);
43873 decimalPrecisionFcn : function(v)
43875 return Math.floor(v);
43878 validateValue : function(value)
43880 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43884 var num = this.parseValue(value);
43887 this.markInvalid(String.format(this.nanText, value));
43891 if(num < this.minValue){
43892 this.markInvalid(String.format(this.minText, this.minValue));
43896 if(num > this.maxValue){
43897 this.markInvalid(String.format(this.maxText, this.maxValue));
43904 validate : function()
43906 if(this.disabled || this.allowBlank){
43911 var currency = this.getCurrency();
43913 if(this.validateValue(this.getRawValue()) && currency.length){
43918 this.markInvalid();
43922 getName: function()
43927 beforeBlur : function()
43933 var v = this.parseValue(this.getRawValue());
43940 onBlur : function()
43944 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43945 //this.el.removeClass(this.focusClass);
43948 this.hasFocus = false;
43950 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43954 var v = this.getValue();
43956 if(String(v) !== String(this.startValue)){
43957 this.fireEvent('change', this, v, this.startValue);
43960 this.fireEvent("blur", this);
43963 inputEl : function()
43965 return this.el.select('.roo-money-amount-input', true).first();
43968 currencyEl : function()
43970 return this.el.select('.roo-money-currency-input', true).first();
43973 hiddenEl : function()
43975 return this.el.select('input.hidden-number-input',true).first();
43979 * @class Roo.bootstrap.BezierSignature
43980 * @extends Roo.bootstrap.Component
43981 * Bootstrap BezierSignature class
43982 * This script refer to:
43983 * Title: Signature Pad
43985 * Availability: https://github.com/szimek/signature_pad
43988 * Create a new BezierSignature
43989 * @param {Object} config The config object
43992 Roo.bootstrap.BezierSignature = function(config){
43993 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43999 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44006 mouse_btn_down: true,
44009 * @cfg {int} canvas height
44011 canvas_height: '200px',
44014 * @cfg {float|function} Radius of a single dot.
44019 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44024 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44029 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44034 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44039 * @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.
44041 bg_color: 'rgba(0, 0, 0, 0)',
44044 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44046 dot_color: 'black',
44049 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44051 velocity_filter_weight: 0.7,
44054 * @cfg {function} Callback when stroke begin.
44059 * @cfg {function} Callback when stroke end.
44063 getAutoCreate : function()
44065 var cls = 'roo-signature column';
44068 cls += ' ' + this.cls;
44078 for(var i = 0; i < col_sizes.length; i++) {
44079 if(this[col_sizes[i]]) {
44080 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44090 cls: 'roo-signature-body',
44094 cls: 'roo-signature-body-canvas',
44095 height: this.canvas_height,
44096 width: this.canvas_width
44103 style: 'display: none'
44111 initEvents: function()
44113 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44115 var canvas = this.canvasEl();
44117 // mouse && touch event swapping...
44118 canvas.dom.style.touchAction = 'none';
44119 canvas.dom.style.msTouchAction = 'none';
44121 this.mouse_btn_down = false;
44122 canvas.on('mousedown', this._handleMouseDown, this);
44123 canvas.on('mousemove', this._handleMouseMove, this);
44124 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44126 if (window.PointerEvent) {
44127 canvas.on('pointerdown', this._handleMouseDown, this);
44128 canvas.on('pointermove', this._handleMouseMove, this);
44129 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44132 if ('ontouchstart' in window) {
44133 canvas.on('touchstart', this._handleTouchStart, this);
44134 canvas.on('touchmove', this._handleTouchMove, this);
44135 canvas.on('touchend', this._handleTouchEnd, this);
44138 Roo.EventManager.onWindowResize(this.resize, this, true);
44140 // file input event
44141 this.fileEl().on('change', this.uploadImage, this);
44148 resize: function(){
44150 var canvas = this.canvasEl().dom;
44151 var ctx = this.canvasElCtx();
44152 var img_data = false;
44154 if(canvas.width > 0) {
44155 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44157 // setting canvas width will clean img data
44160 var style = window.getComputedStyle ?
44161 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44163 var padding_left = parseInt(style.paddingLeft) || 0;
44164 var padding_right = parseInt(style.paddingRight) || 0;
44166 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44169 ctx.putImageData(img_data, 0, 0);
44173 _handleMouseDown: function(e)
44175 if (e.browserEvent.which === 1) {
44176 this.mouse_btn_down = true;
44177 this.strokeBegin(e);
44181 _handleMouseMove: function (e)
44183 if (this.mouse_btn_down) {
44184 this.strokeMoveUpdate(e);
44188 _handleMouseUp: function (e)
44190 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44191 this.mouse_btn_down = false;
44196 _handleTouchStart: function (e) {
44198 e.preventDefault();
44199 if (e.browserEvent.targetTouches.length === 1) {
44200 // var touch = e.browserEvent.changedTouches[0];
44201 // this.strokeBegin(touch);
44203 this.strokeBegin(e); // assume e catching the correct xy...
44207 _handleTouchMove: function (e) {
44208 e.preventDefault();
44209 // var touch = event.targetTouches[0];
44210 // _this._strokeMoveUpdate(touch);
44211 this.strokeMoveUpdate(e);
44214 _handleTouchEnd: function (e) {
44215 var wasCanvasTouched = e.target === this.canvasEl().dom;
44216 if (wasCanvasTouched) {
44217 e.preventDefault();
44218 // var touch = event.changedTouches[0];
44219 // _this._strokeEnd(touch);
44224 reset: function () {
44225 this._lastPoints = [];
44226 this._lastVelocity = 0;
44227 this._lastWidth = (this.min_width + this.max_width) / 2;
44228 this.canvasElCtx().fillStyle = this.dot_color;
44231 strokeMoveUpdate: function(e)
44233 this.strokeUpdate(e);
44235 if (this.throttle) {
44236 this.throttleStroke(this.strokeUpdate, this.throttle);
44239 this.strokeUpdate(e);
44243 strokeBegin: function(e)
44245 var newPointGroup = {
44246 color: this.dot_color,
44250 if (typeof this.onBegin === 'function') {
44254 this.curve_data.push(newPointGroup);
44256 this.strokeUpdate(e);
44259 strokeUpdate: function(e)
44261 var rect = this.canvasEl().dom.getBoundingClientRect();
44262 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44263 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44264 var lastPoints = lastPointGroup.points;
44265 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44266 var isLastPointTooClose = lastPoint
44267 ? point.distanceTo(lastPoint) <= this.min_distance
44269 var color = lastPointGroup.color;
44270 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44271 var curve = this.addPoint(point);
44273 this.drawDot({color: color, point: point});
44276 this.drawCurve({color: color, curve: curve});
44286 strokeEnd: function(e)
44288 this.strokeUpdate(e);
44289 if (typeof this.onEnd === 'function') {
44294 addPoint: function (point) {
44295 var _lastPoints = this._lastPoints;
44296 _lastPoints.push(point);
44297 if (_lastPoints.length > 2) {
44298 if (_lastPoints.length === 3) {
44299 _lastPoints.unshift(_lastPoints[0]);
44301 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44302 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44303 _lastPoints.shift();
44309 calculateCurveWidths: function (startPoint, endPoint) {
44310 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44311 (1 - this.velocity_filter_weight) * this._lastVelocity;
44313 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44316 start: this._lastWidth
44319 this._lastVelocity = velocity;
44320 this._lastWidth = newWidth;
44324 drawDot: function (_a) {
44325 var color = _a.color, point = _a.point;
44326 var ctx = this.canvasElCtx();
44327 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44329 this.drawCurveSegment(point.x, point.y, width);
44331 ctx.fillStyle = color;
44335 drawCurve: function (_a) {
44336 var color = _a.color, curve = _a.curve;
44337 var ctx = this.canvasElCtx();
44338 var widthDelta = curve.endWidth - curve.startWidth;
44339 var drawSteps = Math.floor(curve.length()) * 2;
44341 ctx.fillStyle = color;
44342 for (var i = 0; i < drawSteps; i += 1) {
44343 var t = i / drawSteps;
44349 var x = uuu * curve.startPoint.x;
44350 x += 3 * uu * t * curve.control1.x;
44351 x += 3 * u * tt * curve.control2.x;
44352 x += ttt * curve.endPoint.x;
44353 var y = uuu * curve.startPoint.y;
44354 y += 3 * uu * t * curve.control1.y;
44355 y += 3 * u * tt * curve.control2.y;
44356 y += ttt * curve.endPoint.y;
44357 var width = curve.startWidth + ttt * widthDelta;
44358 this.drawCurveSegment(x, y, width);
44364 drawCurveSegment: function (x, y, width) {
44365 var ctx = this.canvasElCtx();
44367 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44368 this.is_empty = false;
44373 var ctx = this.canvasElCtx();
44374 var canvas = this.canvasEl().dom;
44375 ctx.fillStyle = this.bg_color;
44376 ctx.clearRect(0, 0, canvas.width, canvas.height);
44377 ctx.fillRect(0, 0, canvas.width, canvas.height);
44378 this.curve_data = [];
44380 this.is_empty = true;
44385 return this.el.select('input',true).first();
44388 canvasEl: function()
44390 return this.el.select('canvas',true).first();
44393 canvasElCtx: function()
44395 return this.el.select('canvas',true).first().dom.getContext('2d');
44398 getImage: function(type)
44400 if(this.is_empty) {
44405 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44408 drawFromImage: function(img_src)
44410 var img = new Image();
44412 img.onload = function(){
44413 this.canvasElCtx().drawImage(img, 0, 0);
44418 this.is_empty = false;
44421 selectImage: function()
44423 this.fileEl().dom.click();
44426 uploadImage: function(e)
44428 var reader = new FileReader();
44430 reader.onload = function(e){
44431 var img = new Image();
44432 img.onload = function(){
44434 this.canvasElCtx().drawImage(img, 0, 0);
44436 img.src = e.target.result;
44439 reader.readAsDataURL(e.target.files[0]);
44442 // Bezier Point Constructor
44443 Point: (function () {
44444 function Point(x, y, time) {
44447 this.time = time || Date.now();
44449 Point.prototype.distanceTo = function (start) {
44450 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44452 Point.prototype.equals = function (other) {
44453 return this.x === other.x && this.y === other.y && this.time === other.time;
44455 Point.prototype.velocityFrom = function (start) {
44456 return this.time !== start.time
44457 ? this.distanceTo(start) / (this.time - start.time)
44464 // Bezier Constructor
44465 Bezier: (function () {
44466 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44467 this.startPoint = startPoint;
44468 this.control2 = control2;
44469 this.control1 = control1;
44470 this.endPoint = endPoint;
44471 this.startWidth = startWidth;
44472 this.endWidth = endWidth;
44474 Bezier.fromPoints = function (points, widths, scope) {
44475 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44476 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44477 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44479 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44480 var dx1 = s1.x - s2.x;
44481 var dy1 = s1.y - s2.y;
44482 var dx2 = s2.x - s3.x;
44483 var dy2 = s2.y - s3.y;
44484 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44485 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44486 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44487 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44488 var dxm = m1.x - m2.x;
44489 var dym = m1.y - m2.y;
44490 var k = l2 / (l1 + l2);
44491 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44492 var tx = s2.x - cm.x;
44493 var ty = s2.y - cm.y;
44495 c1: new scope.Point(m1.x + tx, m1.y + ty),
44496 c2: new scope.Point(m2.x + tx, m2.y + ty)
44499 Bezier.prototype.length = function () {
44504 for (var i = 0; i <= steps; i += 1) {
44506 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44507 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44509 var xdiff = cx - px;
44510 var ydiff = cy - py;
44511 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44518 Bezier.prototype.point = function (t, start, c1, c2, end) {
44519 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44520 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44521 + (3.0 * c2 * (1.0 - t) * t * t)
44522 + (end * t * t * t);
44527 throttleStroke: function(fn, wait) {
44528 if (wait === void 0) { wait = 250; }
44530 var timeout = null;
44534 var later = function () {
44535 previous = Date.now();
44537 result = fn.apply(storedContext, storedArgs);
44539 storedContext = null;
44543 return function wrapper() {
44545 for (var _i = 0; _i < arguments.length; _i++) {
44546 args[_i] = arguments[_i];
44548 var now = Date.now();
44549 var remaining = wait - (now - previous);
44550 storedContext = this;
44552 if (remaining <= 0 || remaining > wait) {
44554 clearTimeout(timeout);
44558 result = fn.apply(storedContext, storedArgs);
44560 storedContext = null;
44564 else if (!timeout) {
44565 timeout = window.setTimeout(later, remaining);