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');
1357 this.el.attr("disabled", false);
1361 * Disable this button
1363 disable : function()
1365 this.disabled = true;
1366 this.el.addClass('disabled');
1367 this.el.attr("disabled", "disabled")
1370 * sets the active state on/off,
1371 * @param {Boolean} state (optional) Force a particular state
1373 setActive : function(v) {
1375 this.el[v ? 'addClass' : 'removeClass']('active');
1379 * toggles the current active state
1381 toggleActive : function(e)
1383 this.setActive(!this.pressed); // this modifies pressed...
1384 this.fireEvent('toggle', this, e, this.pressed);
1387 * get the current active state
1388 * @return {boolean} true if it's active
1390 isActive : function()
1392 return this.el.hasClass('active');
1395 * set the text of the first selected button
1397 setText : function(str)
1399 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1402 * get the text of the first selected button
1404 getText : function()
1406 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1409 setWeight : function(str)
1411 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1412 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1414 var outline = this.outline ? 'outline-' : '';
1415 if (str == 'default') {
1416 this.el.addClass('btn-default btn-outline-secondary');
1419 this.el.addClass('btn-' + outline + str);
1424 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1426 Roo.bootstrap.Button.weights = [
1446 * @class Roo.bootstrap.Column
1447 * @extends Roo.bootstrap.Component
1448 * Bootstrap Column class
1449 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1450 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1451 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1452 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1453 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1454 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1455 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1456 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1459 * @cfg {Boolean} hidden (true|false) hide the element
1460 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1461 * @cfg {String} fa (ban|check|...) font awesome icon
1462 * @cfg {Number} fasize (1|2|....) font awsome size
1464 * @cfg {String} icon (info-sign|check|...) glyphicon name
1466 * @cfg {String} html content of column.
1469 * Create a new Column
1470 * @param {Object} config The config object
1473 Roo.bootstrap.Column = function(config){
1474 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1477 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1495 getAutoCreate : function(){
1496 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1504 var sizes = ['xs','sm','md','lg'];
1505 sizes.map(function(size ,ix){
1506 //Roo.log( size + ':' + settings[size]);
1508 if (settings[size+'off'] !== false) {
1509 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1512 if (settings[size] === false) {
1516 if (!settings[size]) { // 0 = hidden
1517 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1519 for (var i = ix; i > -1; i--) {
1520 cfg.cls += ' d-' + sizes[i] + '-none';
1526 cfg.cls += ' col-' + size + '-' + settings[size] + (
1527 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1533 cfg.cls += ' hidden';
1536 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1537 cfg.cls +=' alert alert-' + this.alert;
1541 if (this.html.length) {
1542 cfg.html = this.html;
1546 if (this.fasize > 1) {
1547 fasize = ' fa-' + this.fasize + 'x';
1549 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1554 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1573 * @class Roo.bootstrap.Container
1574 * @extends Roo.bootstrap.Component
1575 * Bootstrap Container class
1576 * @cfg {Boolean} jumbotron is it a jumbotron element
1577 * @cfg {String} html content of element
1578 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1579 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1580 * @cfg {String} header content of header (for panel)
1581 * @cfg {String} footer content of footer (for panel)
1582 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1583 * @cfg {String} tag (header|aside|section) type of HTML tag.
1584 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1585 * @cfg {String} fa font awesome icon
1586 * @cfg {String} icon (info-sign|check|...) glyphicon name
1587 * @cfg {Boolean} hidden (true|false) hide the element
1588 * @cfg {Boolean} expandable (true|false) default false
1589 * @cfg {Boolean} expanded (true|false) default true
1590 * @cfg {String} rheader contet on the right of header
1591 * @cfg {Boolean} clickable (true|false) default false
1595 * Create a new Container
1596 * @param {Object} config The config object
1599 Roo.bootstrap.Container = function(config){
1600 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1606 * After the panel has been expand
1608 * @param {Roo.bootstrap.Container} this
1613 * After the panel has been collapsed
1615 * @param {Roo.bootstrap.Container} this
1620 * When a element is chick
1621 * @param {Roo.bootstrap.Container} this
1622 * @param {Roo.EventObject} e
1628 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1646 getChildContainer : function() {
1652 if (this.panel.length) {
1653 return this.el.select('.panel-body',true).first();
1660 getAutoCreate : function(){
1663 tag : this.tag || 'div',
1667 if (this.jumbotron) {
1668 cfg.cls = 'jumbotron';
1673 // - this is applied by the parent..
1675 // cfg.cls = this.cls + '';
1678 if (this.sticky.length) {
1680 var bd = Roo.get(document.body);
1681 if (!bd.hasClass('bootstrap-sticky')) {
1682 bd.addClass('bootstrap-sticky');
1683 Roo.select('html',true).setStyle('height', '100%');
1686 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1690 if (this.well.length) {
1691 switch (this.well) {
1694 cfg.cls +=' well well-' +this.well;
1703 cfg.cls += ' hidden';
1707 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1708 cfg.cls +=' alert alert-' + this.alert;
1713 if (this.panel.length) {
1714 cfg.cls += ' panel panel-' + this.panel;
1716 if (this.header.length) {
1720 if(this.expandable){
1722 cfg.cls = cfg.cls + ' expandable';
1726 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1734 cls : 'panel-title',
1735 html : (this.expandable ? ' ' : '') + this.header
1739 cls: 'panel-header-right',
1745 cls : 'panel-heading',
1746 style : this.expandable ? 'cursor: pointer' : '',
1754 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1759 if (this.footer.length) {
1761 cls : 'panel-footer',
1770 body.html = this.html || cfg.html;
1771 // prefix with the icons..
1773 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1776 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1781 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1782 cfg.cls = 'container';
1788 initEvents: function()
1790 if(this.expandable){
1791 var headerEl = this.headerEl();
1794 headerEl.on('click', this.onToggleClick, this);
1799 this.el.on('click', this.onClick, this);
1804 onToggleClick : function()
1806 var headerEl = this.headerEl();
1822 if(this.fireEvent('expand', this)) {
1824 this.expanded = true;
1826 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1828 this.el.select('.panel-body',true).first().removeClass('hide');
1830 var toggleEl = this.toggleEl();
1836 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1841 collapse : function()
1843 if(this.fireEvent('collapse', this)) {
1845 this.expanded = false;
1847 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1848 this.el.select('.panel-body',true).first().addClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1860 toggleEl : function()
1862 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1866 return this.el.select('.panel-heading .fa',true).first();
1869 headerEl : function()
1871 if(!this.el || !this.panel.length || !this.header.length){
1875 return this.el.select('.panel-heading',true).first()
1880 if(!this.el || !this.panel.length){
1884 return this.el.select('.panel-body',true).first()
1887 titleEl : function()
1889 if(!this.el || !this.panel.length || !this.header.length){
1893 return this.el.select('.panel-title',true).first();
1896 setTitle : function(v)
1898 var titleEl = this.titleEl();
1904 titleEl.dom.innerHTML = v;
1907 getTitle : function()
1910 var titleEl = this.titleEl();
1916 return titleEl.dom.innerHTML;
1919 setRightTitle : function(v)
1921 var t = this.el.select('.panel-header-right',true).first();
1927 t.dom.innerHTML = v;
1930 onClick : function(e)
1934 this.fireEvent('click', this, e);
1941 * This is BS4's Card element.. - similar to our containers probably..
1945 * @class Roo.bootstrap.Card
1946 * @extends Roo.bootstrap.Component
1947 * Bootstrap Card class
1950 * possible... may not be implemented..
1951 * @cfg {String} header_image src url of image.
1952 * @cfg {String|Object} header
1953 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1954 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1956 * @cfg {String} title
1957 * @cfg {String} subtitle
1958 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1959 * @cfg {String} footer
1961 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1963 * @cfg {String} margin (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1968 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1969 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1971 * @cfg {String} padding (0|1|2|3|4|5)
1972 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1973 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1974 * @cfg {String} padding_left (0|1|2|3|4|5)
1975 * @cfg {String} padding_right (0|1|2|3|4|5)
1976 * @cfg {String} padding_x (0|1|2|3|4|5)
1977 * @cfg {String} padding_y (0|1|2|3|4|5)
1979 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1985 * @config {Boolean} dragable if this card can be dragged.
1986 * @config {String} drag_group group for drag
1987 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1988 * @config {String} drop_group group for drag
1990 * @config {Boolean} collapsable can the body be collapsed.
1991 * @config {Boolean} collapsed is the body collapsed when rendered...
1992 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1993 * @config {Boolean} rotated is the body rotated when rendered...
1996 * Create a new Container
1997 * @param {Object} config The config object
2000 Roo.bootstrap.Card = function(config){
2001 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2007 * When a element a card is dropped
2008 * @param {Roo.bootstrap.Card} this
2011 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2012 * @param {String} position 'above' or 'below'
2013 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2019 * When a element a card is rotate
2020 * @param {Roo.bootstrap.Element} this
2021 * @param {Roo.Element} n the node being dropped?
2022 * @param {Boolean} rotate status
2030 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2035 margin: '', /// may be better in component?
2065 collapsable : false,
2074 childContainer : false,
2075 dropEl : false, /// the dom placeholde element that indicates drop location.
2076 containerEl: false, // body container
2077 bodyEl: false, // card-body
2078 headerContainerEl : false, //
2080 header_imageEl : false,
2082 layoutCls : function()
2086 Roo.log(this.margin_bottom.length);
2087 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2088 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2090 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2091 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2093 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2094 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2098 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2099 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2100 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2104 // more generic support?
2112 // Roo.log("Call onRender: " + this.xtype);
2113 /* We are looking at something like this.
2115 <img src="..." class="card-img-top" alt="...">
2116 <div class="card-body">
2117 <h5 class="card-title">Card title</h5>
2118 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2120 >> this bit is really the body...
2121 <div> << we will ad dthis in hopefully it will not break shit.
2123 ** card text does not actually have any styling...
2125 <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>
2128 <a href="#" class="card-link">Card link</a>
2131 <div class="card-footer">
2132 <small class="text-muted">Last updated 3 mins ago</small>
2136 getAutoCreate : function(){
2144 if (this.weight.length && this.weight != 'light') {
2145 cfg.cls += ' text-white';
2147 cfg.cls += ' text-dark'; // need as it's nested..
2149 if (this.weight.length) {
2150 cfg.cls += ' bg-' + this.weight;
2153 cfg.cls += ' ' + this.layoutCls();
2156 var hdr_ctr = false;
2157 if (this.header.length) {
2159 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2160 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2168 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2174 if (this.collapsable) {
2177 cls : 'd-block user-select-none',
2181 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2186 hdr.cn.push(hdr_ctr);
2191 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2196 if (this.header_image.length) {
2199 cls : 'card-img-top',
2200 src: this.header_image // escape?
2205 cls : 'card-img-top d-none'
2211 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2215 if (this.collapsable || this.rotateable) {
2218 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2225 if (this.title.length) {
2229 src: this.title // escape?
2233 if (this.subtitle.length) {
2237 src: this.subtitle // escape?
2243 cls : 'roo-card-body-ctr'
2246 if (this.html.length) {
2252 // fixme ? handle objects?
2254 if (this.footer.length) {
2257 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2262 cfg.cn.push({cls : 'card-footer d-none'});
2271 getCardHeader : function()
2273 var ret = this.el.select('.card-header',true).first();
2274 if (ret.hasClass('d-none')) {
2275 ret.removeClass('d-none');
2280 getCardFooter : function()
2282 var ret = this.el.select('.card-footer',true).first();
2283 if (ret.hasClass('d-none')) {
2284 ret.removeClass('d-none');
2289 getCardImageTop : function()
2291 var ret = this.header_imageEl;
2292 if (ret.hasClass('d-none')) {
2293 ret.removeClass('d-none');
2299 getChildContainer : function()
2305 return this.el.select('.roo-card-body-ctr',true).first();
2308 initEvents: function()
2310 this.bodyEl = this.el.select('.card-body',true).first();
2311 this.containerEl = this.getChildContainer();
2313 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2314 containerScroll: true,
2315 ddGroup: this.drag_group || 'default_card_drag_group'
2317 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2319 if (this.dropable) {
2320 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2321 containerScroll: true,
2322 ddGroup: this.drop_group || 'default_card_drag_group'
2324 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2325 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2326 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2327 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2328 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2331 if (this.collapsable) {
2332 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2334 if (this.rotateable) {
2335 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2337 this.collapsableEl = this.el.select('.roo-collapsable').first();
2339 this.footerEl = this.el.select('.card-footer').first();
2340 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2341 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2342 this.headerEl = this.el.select('.card-header',true).first();
2345 this.el.addClass('roo-card-rotated');
2346 this.fireEvent('rotate', this, true);
2348 this.header_imageEl = this.el.select('.card-img-top',true).first();
2349 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2352 getDragData : function(e)
2354 var target = this.getEl();
2356 //this.handleSelection(e);
2361 nodes: this.getEl(),
2366 dragData.ddel = target.dom ; // the div element
2367 Roo.log(target.getWidth( ));
2368 dragData.ddel.style.width = target.getWidth() + 'px';
2375 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2376 * whole Element becomes the target, and this causes the drop gesture to append.
2378 getTargetFromEvent : function(e, dragged_card_el)
2380 var target = e.getTarget();
2381 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2382 target = target.parentNode;
2393 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2394 // see if target is one of the 'cards'...
2397 //Roo.log(this.items.length);
2400 var last_card_n = 0;
2402 for (var i = 0;i< this.items.length;i++) {
2404 if (!this.items[i].el.hasClass('card')) {
2407 pos = this.getDropPoint(e, this.items[i].el.dom);
2409 cards_len = ret.cards.length;
2410 //Roo.log(this.items[i].el.dom.id);
2411 ret.cards.push(this.items[i]);
2413 if (ret.card_n < 0 && pos == 'above') {
2414 ret.position = cards_len > 0 ? 'below' : pos;
2415 ret.items_n = i > 0 ? i - 1 : 0;
2416 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2417 ret.card = ret.cards[ret.card_n];
2420 if (!ret.cards.length) {
2422 ret.position = 'below';
2426 // could not find a card.. stick it at the end..
2427 if (ret.card_n < 0) {
2428 ret.card_n = last_card_n;
2429 ret.card = ret.cards[last_card_n];
2430 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2431 ret.position = 'below';
2434 if (this.items[ret.items_n].el == dragged_card_el) {
2438 if (ret.position == 'below') {
2439 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2441 if (card_after && card_after.el == dragged_card_el) {
2448 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2450 if (card_before && card_before.el == dragged_card_el) {
2457 onNodeEnter : function(n, dd, e, data){
2460 onNodeOver : function(n, dd, e, data)
2463 var target_info = this.getTargetFromEvent(e,data.source.el);
2464 if (target_info === false) {
2465 this.dropPlaceHolder('hide');
2468 Roo.log(['getTargetFromEvent', target_info ]);
2471 this.dropPlaceHolder('show', target_info,data);
2475 onNodeOut : function(n, dd, e, data){
2476 this.dropPlaceHolder('hide');
2479 onNodeDrop : function(n, dd, e, data)
2482 // call drop - return false if
2484 // this could actually fail - if the Network drops..
2485 // we will ignore this at present..- client should probably reload
2486 // the whole set of cards if stuff like that fails.
2489 var info = this.getTargetFromEvent(e,data.source.el);
2490 if (info === false) {
2493 this.dropPlaceHolder('hide');
2499 this.acceptCard(data.source, info.position, info.card, info.items_n);
2503 firstChildCard : function()
2505 for (var i = 0;i< this.items.length;i++) {
2507 if (!this.items[i].el.hasClass('card')) {
2510 return this.items[i];
2512 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2517 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2519 acceptCard : function(move_card, position, next_to_card )
2521 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2525 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2527 move_card.parent().removeCard(move_card);
2530 var dom = move_card.el.dom;
2531 dom.style.width = ''; // clear with - which is set by drag.
2533 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2534 var cardel = next_to_card.el.dom;
2536 if (position == 'above' ) {
2537 cardel.parentNode.insertBefore(dom, cardel);
2538 } else if (cardel.nextSibling) {
2539 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2541 cardel.parentNode.append(dom);
2544 // card container???
2545 this.containerEl.dom.append(dom);
2548 //FIXME HANDLE card = true
2550 // add this to the correct place in items.
2552 // remove Card from items.
2555 if (this.items.length) {
2557 //Roo.log([info.items_n, info.position, this.items.length]);
2558 for (var i =0; i < this.items.length; i++) {
2559 if (i == to_items_n && position == 'above') {
2560 nitems.push(move_card);
2562 nitems.push(this.items[i]);
2563 if (i == to_items_n && position == 'below') {
2564 nitems.push(move_card);
2567 this.items = nitems;
2568 Roo.log(this.items);
2570 this.items.push(move_card);
2573 move_card.parentId = this.id;
2579 removeCard : function(c)
2581 this.items = this.items.filter(function(e) { return e != c });
2584 dom.parentNode.removeChild(dom);
2585 dom.style.width = ''; // clear with - which is set by drag.
2590 /** Decide whether to drop above or below a View node. */
2591 getDropPoint : function(e, n, dd)
2596 if (n == this.containerEl.dom) {
2599 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2600 var c = t + (b - t) / 2;
2601 var y = Roo.lib.Event.getPageY(e);
2608 onToggleCollapse : function(e)
2610 if (this.collapsed) {
2611 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2612 this.collapsableEl.addClass('show');
2613 this.collapsed = false;
2616 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2617 this.collapsableEl.removeClass('show');
2618 this.collapsed = true;
2623 onToggleRotate : function(e)
2625 this.collapsableEl.removeClass('show');
2626 this.footerEl.removeClass('d-none');
2627 this.el.removeClass('roo-card-rotated');
2628 this.el.removeClass('d-none');
2631 this.collapsableEl.addClass('show');
2632 this.rotated = false;
2633 this.fireEvent('rotate', this, this.rotated);
2636 this.el.addClass('roo-card-rotated');
2637 this.footerEl.addClass('d-none');
2638 this.el.select('.roo-collapsable').removeClass('show');
2640 this.rotated = true;
2641 this.fireEvent('rotate', this, this.rotated);
2645 dropPlaceHolder: function (action, info, data)
2647 if (this.dropEl === false) {
2648 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2652 this.dropEl.removeClass(['d-none', 'd-block']);
2653 if (action == 'hide') {
2655 this.dropEl.addClass('d-none');
2658 // FIXME - info.card == true!!!
2659 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2661 if (info.card !== true) {
2662 var cardel = info.card.el.dom;
2664 if (info.position == 'above') {
2665 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2666 } else if (cardel.nextSibling) {
2667 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2669 cardel.parentNode.append(this.dropEl.dom);
2672 // card container???
2673 this.containerEl.dom.append(this.dropEl.dom);
2676 this.dropEl.addClass('d-block roo-card-dropzone');
2678 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2685 setHeaderText: function(html)
2688 if (this.headerContainerEl) {
2689 this.headerContainerEl.dom.innerHTML = html;
2692 onHeaderImageLoad : function(ev, he)
2694 if (!this.header_image_fit_square) {
2698 var hw = he.naturalHeight / he.naturalWidth;
2701 //var w = he.dom.naturalWidth;
2704 he.style.position = 'relative';
2706 var nw = (ww * (1/hw));
2707 Roo.get(he).setSize( ww * (1/hw), ww);
2708 he.style.left = ((ww - nw)/ 2) + 'px';
2709 he.style.position = 'relative';
2720 * Card header - holder for the card header elements.
2725 * @class Roo.bootstrap.CardHeader
2726 * @extends Roo.bootstrap.Element
2727 * Bootstrap CardHeader class
2729 * Create a new Card Header - that you can embed children into
2730 * @param {Object} config The config object
2733 Roo.bootstrap.CardHeader = function(config){
2734 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2737 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2740 container_method : 'getCardHeader'
2753 * Card footer - holder for the card footer elements.
2758 * @class Roo.bootstrap.CardFooter
2759 * @extends Roo.bootstrap.Element
2760 * Bootstrap CardFooter class
2762 * Create a new Card Footer - that you can embed children into
2763 * @param {Object} config The config object
2766 Roo.bootstrap.CardFooter = function(config){
2767 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2770 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2773 container_method : 'getCardFooter'
2786 * Card header - holder for the card header elements.
2791 * @class Roo.bootstrap.CardImageTop
2792 * @extends Roo.bootstrap.Element
2793 * Bootstrap CardImageTop class
2795 * Create a new Card Image Top container
2796 * @param {Object} config The config object
2799 Roo.bootstrap.CardImageTop = function(config){
2800 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2803 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2806 container_method : 'getCardImageTop'
2824 * @class Roo.bootstrap.Img
2825 * @extends Roo.bootstrap.Component
2826 * Bootstrap Img class
2827 * @cfg {Boolean} imgResponsive false | true
2828 * @cfg {String} border rounded | circle | thumbnail
2829 * @cfg {String} src image source
2830 * @cfg {String} alt image alternative text
2831 * @cfg {String} href a tag href
2832 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2833 * @cfg {String} xsUrl xs image source
2834 * @cfg {String} smUrl sm image source
2835 * @cfg {String} mdUrl md image source
2836 * @cfg {String} lgUrl lg image source
2839 * Create a new Input
2840 * @param {Object} config The config object
2843 Roo.bootstrap.Img = function(config){
2844 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2850 * The img click event for the img.
2851 * @param {Roo.EventObject} e
2857 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2859 imgResponsive: true,
2869 getAutoCreate : function()
2871 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2872 return this.createSingleImg();
2877 cls: 'roo-image-responsive-group',
2882 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2884 if(!_this[size + 'Url']){
2890 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2891 html: _this.html || cfg.html,
2892 src: _this[size + 'Url']
2895 img.cls += ' roo-image-responsive-' + size;
2897 var s = ['xs', 'sm', 'md', 'lg'];
2899 s.splice(s.indexOf(size), 1);
2901 Roo.each(s, function(ss){
2902 img.cls += ' hidden-' + ss;
2905 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2906 cfg.cls += ' img-' + _this.border;
2910 cfg.alt = _this.alt;
2923 a.target = _this.target;
2927 cfg.cn.push((_this.href) ? a : img);
2934 createSingleImg : function()
2938 cls: (this.imgResponsive) ? 'img-responsive' : '',
2940 src : 'about:blank' // just incase src get's set to undefined?!?
2943 cfg.html = this.html || cfg.html;
2945 cfg.src = this.src || cfg.src;
2947 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2948 cfg.cls += ' img-' + this.border;
2965 a.target = this.target;
2970 return (this.href) ? a : cfg;
2973 initEvents: function()
2976 this.el.on('click', this.onClick, this);
2981 onClick : function(e)
2983 Roo.log('img onclick');
2984 this.fireEvent('click', this, e);
2987 * Sets the url of the image - used to update it
2988 * @param {String} url the url of the image
2991 setSrc : function(url)
2995 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2996 this.el.dom.src = url;
3000 this.el.select('img', true).first().dom.src = url;
3016 * @class Roo.bootstrap.Link
3017 * @extends Roo.bootstrap.Component
3018 * Bootstrap Link Class
3019 * @cfg {String} alt image alternative text
3020 * @cfg {String} href a tag href
3021 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3022 * @cfg {String} html the content of the link.
3023 * @cfg {String} anchor name for the anchor link
3024 * @cfg {String} fa - favicon
3026 * @cfg {Boolean} preventDefault (true | false) default false
3030 * Create a new Input
3031 * @param {Object} config The config object
3034 Roo.bootstrap.Link = function(config){
3035 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3041 * The img click event for the img.
3042 * @param {Roo.EventObject} e
3048 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3052 preventDefault: false,
3058 getAutoCreate : function()
3060 var html = this.html || '';
3062 if (this.fa !== false) {
3063 html = '<i class="fa fa-' + this.fa + '"></i>';
3068 // anchor's do not require html/href...
3069 if (this.anchor === false) {
3071 cfg.href = this.href || '#';
3073 cfg.name = this.anchor;
3074 if (this.html !== false || this.fa !== false) {
3077 if (this.href !== false) {
3078 cfg.href = this.href;
3082 if(this.alt !== false){
3087 if(this.target !== false) {
3088 cfg.target = this.target;
3094 initEvents: function() {
3096 if(!this.href || this.preventDefault){
3097 this.el.on('click', this.onClick, this);
3101 onClick : function(e)
3103 if(this.preventDefault){
3106 //Roo.log('img onclick');
3107 this.fireEvent('click', this, e);
3120 * @class Roo.bootstrap.Header
3121 * @extends Roo.bootstrap.Component
3122 * Bootstrap Header class
3123 * @cfg {String} html content of header
3124 * @cfg {Number} level (1|2|3|4|5|6) default 1
3127 * Create a new Header
3128 * @param {Object} config The config object
3132 Roo.bootstrap.Header = function(config){
3133 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3136 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3144 getAutoCreate : function(){
3149 tag: 'h' + (1 *this.level),
3150 html: this.html || ''
3162 * Ext JS Library 1.1.1
3163 * Copyright(c) 2006-2007, Ext JS, LLC.
3165 * Originally Released Under LGPL - original licence link has changed is not relivant.
3168 * <script type="text/javascript">
3172 * @class Roo.bootstrap.MenuMgr
3173 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3176 Roo.bootstrap.MenuMgr = function(){
3177 var menus, active, groups = {}, attached = false, lastShow = new Date();
3179 // private - called when first menu is created
3182 active = new Roo.util.MixedCollection();
3183 Roo.get(document).addKeyListener(27, function(){
3184 if(active.length > 0){
3192 if(active && active.length > 0){
3193 var c = active.clone();
3203 if(active.length < 1){
3204 Roo.get(document).un("mouseup", onMouseDown);
3212 var last = active.last();
3213 lastShow = new Date();
3216 Roo.get(document).on("mouseup", onMouseDown);
3221 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3222 m.parentMenu.activeChild = m;
3223 }else if(last && last.isVisible()){
3224 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3229 function onBeforeHide(m){
3231 m.activeChild.hide();
3233 if(m.autoHideTimer){
3234 clearTimeout(m.autoHideTimer);
3235 delete m.autoHideTimer;
3240 function onBeforeShow(m){
3241 var pm = m.parentMenu;
3242 if(!pm && !m.allowOtherMenus){
3244 }else if(pm && pm.activeChild && active != m){
3245 pm.activeChild.hide();
3249 // private this should really trigger on mouseup..
3250 function onMouseDown(e){
3251 Roo.log("on Mouse Up");
3253 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3254 Roo.log("MenuManager hideAll");
3263 function onBeforeCheck(mi, state){
3265 var g = groups[mi.group];
3266 for(var i = 0, l = g.length; i < l; i++){
3268 g[i].setChecked(false);
3277 * Hides all menus that are currently visible
3279 hideAll : function(){
3284 register : function(menu){
3288 menus[menu.id] = menu;
3289 menu.on("beforehide", onBeforeHide);
3290 menu.on("hide", onHide);
3291 menu.on("beforeshow", onBeforeShow);
3292 menu.on("show", onShow);
3294 if(g && menu.events["checkchange"]){
3298 groups[g].push(menu);
3299 menu.on("checkchange", onCheck);
3304 * Returns a {@link Roo.menu.Menu} object
3305 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3306 * be used to generate and return a new Menu instance.
3308 get : function(menu){
3309 if(typeof menu == "string"){ // menu id
3311 }else if(menu.events){ // menu instance
3314 /*else if(typeof menu.length == 'number'){ // array of menu items?
3315 return new Roo.bootstrap.Menu({items:menu});
3316 }else{ // otherwise, must be a config
3317 return new Roo.bootstrap.Menu(menu);
3324 unregister : function(menu){
3325 delete menus[menu.id];
3326 menu.un("beforehide", onBeforeHide);
3327 menu.un("hide", onHide);
3328 menu.un("beforeshow", onBeforeShow);
3329 menu.un("show", onShow);
3331 if(g && menu.events["checkchange"]){
3332 groups[g].remove(menu);
3333 menu.un("checkchange", onCheck);
3338 registerCheckable : function(menuItem){
3339 var g = menuItem.group;
3344 groups[g].push(menuItem);
3345 menuItem.on("beforecheckchange", onBeforeCheck);
3350 unregisterCheckable : function(menuItem){
3351 var g = menuItem.group;
3353 groups[g].remove(menuItem);
3354 menuItem.un("beforecheckchange", onBeforeCheck);
3366 * @class Roo.bootstrap.Menu
3367 * @extends Roo.bootstrap.Component
3368 * Bootstrap Menu class - container for MenuItems
3369 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3370 * @cfg {bool} hidden if the menu should be hidden when rendered.
3371 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3372 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3376 * @param {Object} config The config object
3380 Roo.bootstrap.Menu = function(config){
3381 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3382 if (this.registerMenu && this.type != 'treeview') {
3383 Roo.bootstrap.MenuMgr.register(this);
3390 * Fires before this menu is displayed (return false to block)
3391 * @param {Roo.menu.Menu} this
3396 * Fires before this menu is hidden (return false to block)
3397 * @param {Roo.menu.Menu} this
3402 * Fires after this menu is displayed
3403 * @param {Roo.menu.Menu} this
3408 * Fires after this menu is hidden
3409 * @param {Roo.menu.Menu} this
3414 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3415 * @param {Roo.menu.Menu} this
3416 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3417 * @param {Roo.EventObject} e
3422 * Fires when the mouse is hovering over this menu
3423 * @param {Roo.menu.Menu} this
3424 * @param {Roo.EventObject} e
3425 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3430 * Fires when the mouse exits this menu
3431 * @param {Roo.menu.Menu} this
3432 * @param {Roo.EventObject} e
3433 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3438 * Fires when a menu item contained in this menu is clicked
3439 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3440 * @param {Roo.EventObject} e
3444 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3447 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3451 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3454 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3456 registerMenu : true,
3458 menuItems :false, // stores the menu items..
3468 getChildContainer : function() {
3472 getAutoCreate : function(){
3474 //if (['right'].indexOf(this.align)!==-1) {
3475 // cfg.cn[1].cls += ' pull-right'
3481 cls : 'dropdown-menu' ,
3482 style : 'z-index:1000'
3486 if (this.type === 'submenu') {
3487 cfg.cls = 'submenu active';
3489 if (this.type === 'treeview') {
3490 cfg.cls = 'treeview-menu';
3495 initEvents : function() {
3497 // Roo.log("ADD event");
3498 // Roo.log(this.triggerEl.dom);
3500 this.triggerEl.on('click', this.onTriggerClick, this);
3502 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3505 if (this.triggerEl.hasClass('nav-item')) {
3506 // dropdown toggle on the 'a' in BS4?
3507 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3509 this.triggerEl.addClass('dropdown-toggle');
3512 this.el.on('touchstart' , this.onTouch, this);
3514 this.el.on('click' , this.onClick, this);
3516 this.el.on("mouseover", this.onMouseOver, this);
3517 this.el.on("mouseout", this.onMouseOut, this);
3521 findTargetItem : function(e)
3523 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3527 //Roo.log(t); Roo.log(t.id);
3529 //Roo.log(this.menuitems);
3530 return this.menuitems.get(t.id);
3532 //return this.items.get(t.menuItemId);
3538 onTouch : function(e)
3540 Roo.log("menu.onTouch");
3541 //e.stopEvent(); this make the user popdown broken
3545 onClick : function(e)
3547 Roo.log("menu.onClick");
3549 var t = this.findTargetItem(e);
3550 if(!t || t.isContainer){
3555 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3556 if(t == this.activeItem && t.shouldDeactivate(e)){
3557 this.activeItem.deactivate();
3558 delete this.activeItem;
3562 this.setActiveItem(t, true);
3570 Roo.log('pass click event');
3574 this.fireEvent("click", this, t, e);
3578 if(!t.href.length || t.href == '#'){
3579 (function() { _this.hide(); }).defer(100);
3584 onMouseOver : function(e){
3585 var t = this.findTargetItem(e);
3588 // if(t.canActivate && !t.disabled){
3589 // this.setActiveItem(t, true);
3593 this.fireEvent("mouseover", this, e, t);
3595 isVisible : function(){
3596 return !this.hidden;
3598 onMouseOut : function(e){
3599 var t = this.findTargetItem(e);
3602 // if(t == this.activeItem && t.shouldDeactivate(e)){
3603 // this.activeItem.deactivate();
3604 // delete this.activeItem;
3607 this.fireEvent("mouseout", this, e, t);
3612 * Displays this menu relative to another element
3613 * @param {String/HTMLElement/Roo.Element} element The element to align to
3614 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3615 * the element (defaults to this.defaultAlign)
3616 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3618 show : function(el, pos, parentMenu)
3620 if (false === this.fireEvent("beforeshow", this)) {
3621 Roo.log("show canceled");
3624 this.parentMenu = parentMenu;
3629 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3632 * Displays this menu at a specific xy position
3633 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3634 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3636 showAt : function(xy, parentMenu, /* private: */_e){
3637 this.parentMenu = parentMenu;
3642 this.fireEvent("beforeshow", this);
3643 //xy = this.el.adjustForConstraints(xy);
3647 this.hideMenuItems();
3648 this.hidden = false;
3649 this.triggerEl.addClass('open');
3650 this.el.addClass('show');
3652 // reassign x when hitting right
3653 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3654 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3657 // reassign y when hitting bottom
3658 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3659 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3662 // but the list may align on trigger left or trigger top... should it be a properity?
3664 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3669 this.fireEvent("show", this);
3675 this.doFocus.defer(50, this);
3679 doFocus : function(){
3681 this.focusEl.focus();
3686 * Hides this menu and optionally all parent menus
3687 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3689 hide : function(deep)
3691 if (false === this.fireEvent("beforehide", this)) {
3692 Roo.log("hide canceled");
3695 this.hideMenuItems();
3696 if(this.el && this.isVisible()){
3698 if(this.activeItem){
3699 this.activeItem.deactivate();
3700 this.activeItem = null;
3702 this.triggerEl.removeClass('open');;
3703 this.el.removeClass('show');
3705 this.fireEvent("hide", this);
3707 if(deep === true && this.parentMenu){
3708 this.parentMenu.hide(true);
3712 onTriggerClick : function(e)
3714 Roo.log('trigger click');
3716 var target = e.getTarget();
3718 Roo.log(target.nodeName.toLowerCase());
3720 if(target.nodeName.toLowerCase() === 'i'){
3726 onTriggerPress : function(e)
3728 Roo.log('trigger press');
3729 //Roo.log(e.getTarget());
3730 // Roo.log(this.triggerEl.dom);
3732 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3733 var pel = Roo.get(e.getTarget());
3734 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3735 Roo.log('is treeview or dropdown?');
3739 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3743 if (this.isVisible()) {
3748 this.show(this.triggerEl, '?', false);
3751 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3758 hideMenuItems : function()
3760 Roo.log("hide Menu Items");
3765 this.el.select('.open',true).each(function(aa) {
3767 aa.removeClass('open');
3771 addxtypeChild : function (tree, cntr) {
3772 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3774 this.menuitems.add(comp);
3786 this.getEl().dom.innerHTML = '';
3787 this.menuitems.clear();
3801 * @class Roo.bootstrap.MenuItem
3802 * @extends Roo.bootstrap.Component
3803 * Bootstrap MenuItem class
3804 * @cfg {String} html the menu label
3805 * @cfg {String} href the link
3806 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3807 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3808 * @cfg {Boolean} active used on sidebars to highlight active itesm
3809 * @cfg {String} fa favicon to show on left of menu item.
3810 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3814 * Create a new MenuItem
3815 * @param {Object} config The config object
3819 Roo.bootstrap.MenuItem = function(config){
3820 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3825 * The raw click event for the entire grid.
3826 * @param {Roo.bootstrap.MenuItem} this
3827 * @param {Roo.EventObject} e
3833 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3837 preventDefault: false,
3838 isContainer : false,
3842 getAutoCreate : function(){
3844 if(this.isContainer){
3847 cls: 'dropdown-menu-item '
3857 cls : 'dropdown-item',
3862 if (this.fa !== false) {
3865 cls : 'fa fa-' + this.fa
3874 cls: 'dropdown-menu-item',
3877 if (this.parent().type == 'treeview') {
3878 cfg.cls = 'treeview-menu';
3881 cfg.cls += ' active';
3886 anc.href = this.href || cfg.cn[0].href ;
3887 ctag.html = this.html || cfg.cn[0].html ;
3891 initEvents: function()
3893 if (this.parent().type == 'treeview') {
3894 this.el.select('a').on('click', this.onClick, this);
3898 this.menu.parentType = this.xtype;
3899 this.menu.triggerEl = this.el;
3900 this.menu = this.addxtype(Roo.apply({}, this.menu));
3904 onClick : function(e)
3906 Roo.log('item on click ');
3908 if(this.preventDefault){
3911 //this.parent().hideMenuItems();
3913 this.fireEvent('click', this, e);
3932 * @class Roo.bootstrap.MenuSeparator
3933 * @extends Roo.bootstrap.Component
3934 * Bootstrap MenuSeparator class
3937 * Create a new MenuItem
3938 * @param {Object} config The config object
3942 Roo.bootstrap.MenuSeparator = function(config){
3943 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3946 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3948 getAutoCreate : function(){
3967 * @class Roo.bootstrap.Modal
3968 * @extends Roo.bootstrap.Component
3969 * Bootstrap Modal class
3970 * @cfg {String} title Title of dialog
3971 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3972 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3973 * @cfg {Boolean} specificTitle default false
3974 * @cfg {Array} buttons Array of buttons or standard button set..
3975 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3976 * @cfg {Boolean} animate default true
3977 * @cfg {Boolean} allow_close default true
3978 * @cfg {Boolean} fitwindow default false
3979 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3980 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3981 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3982 * @cfg {String} size (sm|lg|xl) default empty
3983 * @cfg {Number} max_width set the max width of modal
3984 * @cfg {Boolean} editableTitle can the title be edited
3989 * Create a new Modal Dialog
3990 * @param {Object} config The config object
3993 Roo.bootstrap.Modal = function(config){
3994 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3999 * The raw btnclick event for the button
4000 * @param {Roo.EventObject} e
4005 * Fire when dialog resize
4006 * @param {Roo.bootstrap.Modal} this
4007 * @param {Roo.EventObject} e
4011 * @event titlechanged
4012 * Fire when the editable title has been changed
4013 * @param {Roo.bootstrap.Modal} this
4014 * @param {Roo.EventObject} value
4016 "titlechanged" : true
4019 this.buttons = this.buttons || [];
4022 this.tmpl = Roo.factory(this.tmpl);
4027 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4029 title : 'test dialog',
4039 specificTitle: false,
4041 buttonPosition: 'right',
4063 editableTitle : false,
4065 onRender : function(ct, position)
4067 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4070 var cfg = Roo.apply({}, this.getAutoCreate());
4073 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4075 //if (!cfg.name.length) {
4079 cfg.cls += ' ' + this.cls;
4082 cfg.style = this.style;
4084 this.el = Roo.get(document.body).createChild(cfg, position);
4086 //var type = this.el.dom.type;
4089 if(this.tabIndex !== undefined){
4090 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4093 this.dialogEl = this.el.select('.modal-dialog',true).first();
4094 this.bodyEl = this.el.select('.modal-body',true).first();
4095 this.closeEl = this.el.select('.modal-header .close', true).first();
4096 this.headerEl = this.el.select('.modal-header',true).first();
4097 this.titleEl = this.el.select('.modal-title',true).first();
4098 this.footerEl = this.el.select('.modal-footer',true).first();
4100 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4102 //this.el.addClass("x-dlg-modal");
4104 if (this.buttons.length) {
4105 Roo.each(this.buttons, function(bb) {
4106 var b = Roo.apply({}, bb);
4107 b.xns = b.xns || Roo.bootstrap;
4108 b.xtype = b.xtype || 'Button';
4109 if (typeof(b.listeners) == 'undefined') {
4110 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4113 var btn = Roo.factory(b);
4115 btn.render(this.getButtonContainer());
4119 // render the children.
4122 if(typeof(this.items) != 'undefined'){
4123 var items = this.items;
4126 for(var i =0;i < items.length;i++) {
4127 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4131 this.items = nitems;
4133 // where are these used - they used to be body/close/footer
4137 //this.el.addClass([this.fieldClass, this.cls]);
4141 getAutoCreate : function()
4143 // we will default to modal-body-overflow - might need to remove or make optional later.
4145 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4146 html : this.html || ''
4151 cls : 'modal-title',
4155 if(this.specificTitle){ // WTF is this?
4160 if (this.allow_close && Roo.bootstrap.version == 3) {
4170 if (this.editableTitle) {
4172 cls: 'form-control roo-editable-title d-none',
4178 if (this.allow_close && Roo.bootstrap.version == 4) {
4188 if(this.size.length){
4189 size = 'modal-' + this.size;
4192 var footer = Roo.bootstrap.version == 3 ?
4194 cls : 'modal-footer',
4198 cls: 'btn-' + this.buttonPosition
4203 { // BS4 uses mr-auto on left buttons....
4204 cls : 'modal-footer'
4215 cls: "modal-dialog " + size,
4218 cls : "modal-content",
4221 cls : 'modal-header',
4236 modal.cls += ' fade';
4242 getChildContainer : function() {
4247 getButtonContainer : function() {
4249 return Roo.bootstrap.version == 4 ?
4250 this.el.select('.modal-footer',true).first()
4251 : this.el.select('.modal-footer div',true).first();
4254 initEvents : function()
4256 if (this.allow_close) {
4257 this.closeEl.on('click', this.hide, this);
4259 Roo.EventManager.onWindowResize(this.resize, this, true);
4260 if (this.editableTitle) {
4261 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4262 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4263 this.headerEditEl.on('keyup', function(e) {
4264 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4265 this.toggleHeaderInput(false)
4268 this.headerEditEl.on('blur', function(e) {
4269 this.toggleHeaderInput(false)
4278 this.maskEl.setSize(
4279 Roo.lib.Dom.getViewWidth(true),
4280 Roo.lib.Dom.getViewHeight(true)
4283 if (this.fitwindow) {
4285 this.dialogEl.setStyle( { 'max-width' : '100%' });
4287 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4288 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4293 if(this.max_width !== 0) {
4295 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4298 this.setSize(w, this.height);
4302 if(this.max_height) {
4303 this.setSize(w,Math.min(
4305 Roo.lib.Dom.getViewportHeight(true) - 60
4311 if(!this.fit_content) {
4312 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4316 this.setSize(w, Math.min(
4318 this.headerEl.getHeight() +
4319 this.footerEl.getHeight() +
4320 this.getChildHeight(this.bodyEl.dom.childNodes),
4321 Roo.lib.Dom.getViewportHeight(true) - 60)
4327 setSize : function(w,h)
4338 if (!this.rendered) {
4341 this.toggleHeaderInput(false);
4342 //this.el.setStyle('display', 'block');
4343 this.el.removeClass('hideing');
4344 this.el.dom.style.display='block';
4346 Roo.get(document.body).addClass('modal-open');
4348 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4351 this.el.addClass('show');
4352 this.el.addClass('in');
4355 this.el.addClass('show');
4356 this.el.addClass('in');
4359 // not sure how we can show data in here..
4361 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4364 Roo.get(document.body).addClass("x-body-masked");
4366 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4367 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4368 this.maskEl.dom.style.display = 'block';
4369 this.maskEl.addClass('show');
4374 this.fireEvent('show', this);
4376 // set zindex here - otherwise it appears to be ignored...
4377 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4380 this.items.forEach( function(e) {
4381 e.layout ? e.layout() : false;
4389 if(this.fireEvent("beforehide", this) !== false){
4391 this.maskEl.removeClass('show');
4393 this.maskEl.dom.style.display = '';
4394 Roo.get(document.body).removeClass("x-body-masked");
4395 this.el.removeClass('in');
4396 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4398 if(this.animate){ // why
4399 this.el.addClass('hideing');
4400 this.el.removeClass('show');
4402 if (!this.el.hasClass('hideing')) {
4403 return; // it's been shown again...
4406 this.el.dom.style.display='';
4408 Roo.get(document.body).removeClass('modal-open');
4409 this.el.removeClass('hideing');
4413 this.el.removeClass('show');
4414 this.el.dom.style.display='';
4415 Roo.get(document.body).removeClass('modal-open');
4418 this.fireEvent('hide', this);
4421 isVisible : function()
4424 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4428 addButton : function(str, cb)
4432 var b = Roo.apply({}, { html : str } );
4433 b.xns = b.xns || Roo.bootstrap;
4434 b.xtype = b.xtype || 'Button';
4435 if (typeof(b.listeners) == 'undefined') {
4436 b.listeners = { click : cb.createDelegate(this) };
4439 var btn = Roo.factory(b);
4441 btn.render(this.getButtonContainer());
4447 setDefaultButton : function(btn)
4449 //this.el.select('.modal-footer').()
4452 resizeTo: function(w,h)
4454 this.dialogEl.setWidth(w);
4456 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4458 this.bodyEl.setHeight(h - diff);
4460 this.fireEvent('resize', this);
4463 setContentSize : function(w, h)
4467 onButtonClick: function(btn,e)
4470 this.fireEvent('btnclick', btn.name, e);
4473 * Set the title of the Dialog
4474 * @param {String} str new Title
4476 setTitle: function(str) {
4477 this.titleEl.dom.innerHTML = str;
4481 * Set the body of the Dialog
4482 * @param {String} str new Title
4484 setBody: function(str) {
4485 this.bodyEl.dom.innerHTML = str;
4488 * Set the body of the Dialog using the template
4489 * @param {Obj} data - apply this data to the template and replace the body contents.
4491 applyBody: function(obj)
4494 Roo.log("Error - using apply Body without a template");
4497 this.tmpl.overwrite(this.bodyEl, obj);
4500 getChildHeight : function(child_nodes)
4504 child_nodes.length == 0
4509 var child_height = 0;
4511 for(var i = 0; i < child_nodes.length; i++) {
4514 * for modal with tabs...
4515 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4517 var layout_childs = child_nodes[i].childNodes;
4519 for(var j = 0; j < layout_childs.length; j++) {
4521 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4523 var layout_body_childs = layout_childs[j].childNodes;
4525 for(var k = 0; k < layout_body_childs.length; k++) {
4527 if(layout_body_childs[k].classList.contains('navbar')) {
4528 child_height += layout_body_childs[k].offsetHeight;
4532 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4534 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4536 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4538 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4539 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4554 child_height += child_nodes[i].offsetHeight;
4555 // Roo.log(child_nodes[i].offsetHeight);
4558 return child_height;
4560 toggleHeaderInput : function(is_edit)
4562 if (!this.editableTitle) {
4563 return; // not editable.
4565 if (is_edit && this.is_header_editing) {
4566 return; // already editing..
4570 this.headerEditEl.dom.value = this.title;
4571 this.headerEditEl.removeClass('d-none');
4572 this.headerEditEl.dom.focus();
4573 this.titleEl.addClass('d-none');
4575 this.is_header_editing = true;
4578 // flip back to not editing.
4579 this.title = this.headerEditEl.dom.value;
4580 this.headerEditEl.addClass('d-none');
4581 this.titleEl.removeClass('d-none');
4582 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4583 this.is_header_editing = false;
4584 this.fireEvent('titlechanged', this, this.title);
4593 Roo.apply(Roo.bootstrap.Modal, {
4595 * Button config that displays a single OK button
4604 * Button config that displays Yes and No buttons
4620 * Button config that displays OK and Cancel buttons
4635 * Button config that displays Yes, No and Cancel buttons
4660 * messagebox - can be used as a replace
4664 * @class Roo.MessageBox
4665 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4669 Roo.Msg.alert('Status', 'Changes saved successfully.');
4671 // Prompt for user data:
4672 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4674 // process text value...
4678 // Show a dialog using config options:
4680 title:'Save Changes?',
4681 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4682 buttons: Roo.Msg.YESNOCANCEL,
4689 Roo.bootstrap.MessageBox = function(){
4690 var dlg, opt, mask, waitTimer;
4691 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4692 var buttons, activeTextEl, bwidth;
4696 var handleButton = function(button){
4698 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4702 var handleHide = function(){
4704 dlg.el.removeClass(opt.cls);
4707 // Roo.TaskMgr.stop(waitTimer);
4708 // waitTimer = null;
4713 var updateButtons = function(b){
4716 buttons["ok"].hide();
4717 buttons["cancel"].hide();
4718 buttons["yes"].hide();
4719 buttons["no"].hide();
4720 dlg.footerEl.hide();
4724 dlg.footerEl.show();
4725 for(var k in buttons){
4726 if(typeof buttons[k] != "function"){
4729 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4730 width += buttons[k].el.getWidth()+15;
4740 var handleEsc = function(d, k, e){
4741 if(opt && opt.closable !== false){
4751 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4752 * @return {Roo.BasicDialog} The BasicDialog element
4754 getDialog : function(){
4756 dlg = new Roo.bootstrap.Modal( {
4759 //constraintoviewport:false,
4761 //collapsible : false,
4766 //buttonAlign:"center",
4767 closeClick : function(){
4768 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4771 handleButton("cancel");
4776 dlg.on("hide", handleHide);
4778 //dlg.addKeyListener(27, handleEsc);
4780 this.buttons = buttons;
4781 var bt = this.buttonText;
4782 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4783 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4784 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4785 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4787 bodyEl = dlg.bodyEl.createChild({
4789 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4790 '<textarea class="roo-mb-textarea"></textarea>' +
4791 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4793 msgEl = bodyEl.dom.firstChild;
4794 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4795 textboxEl.enableDisplayMode();
4796 textboxEl.addKeyListener([10,13], function(){
4797 if(dlg.isVisible() && opt && opt.buttons){
4800 }else if(opt.buttons.yes){
4801 handleButton("yes");
4805 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4806 textareaEl.enableDisplayMode();
4807 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4808 progressEl.enableDisplayMode();
4810 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4811 var pf = progressEl.dom.firstChild;
4813 pp = Roo.get(pf.firstChild);
4814 pp.setHeight(pf.offsetHeight);
4822 * Updates the message box body text
4823 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4824 * the XHTML-compliant non-breaking space character '&#160;')
4825 * @return {Roo.MessageBox} This message box
4827 updateText : function(text)
4829 if(!dlg.isVisible() && !opt.width){
4830 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4831 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4833 msgEl.innerHTML = text || ' ';
4835 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4836 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4838 Math.min(opt.width || cw , this.maxWidth),
4839 Math.max(opt.minWidth || this.minWidth, bwidth)
4842 activeTextEl.setWidth(w);
4844 if(dlg.isVisible()){
4845 dlg.fixedcenter = false;
4847 // to big, make it scroll. = But as usual stupid IE does not support
4850 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4851 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4852 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4854 bodyEl.dom.style.height = '';
4855 bodyEl.dom.style.overflowY = '';
4858 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4860 bodyEl.dom.style.overflowX = '';
4863 dlg.setContentSize(w, bodyEl.getHeight());
4864 if(dlg.isVisible()){
4865 dlg.fixedcenter = true;
4871 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4872 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4873 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4874 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4875 * @return {Roo.MessageBox} This message box
4877 updateProgress : function(value, text){
4879 this.updateText(text);
4882 if (pp) { // weird bug on my firefox - for some reason this is not defined
4883 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4884 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4890 * Returns true if the message box is currently displayed
4891 * @return {Boolean} True if the message box is visible, else false
4893 isVisible : function(){
4894 return dlg && dlg.isVisible();
4898 * Hides the message box if it is displayed
4901 if(this.isVisible()){
4907 * Displays a new message box, or reinitializes an existing message box, based on the config options
4908 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4909 * The following config object properties are supported:
4911 Property Type Description
4912 ---------- --------------- ------------------------------------------------------------------------------------
4913 animEl String/Element An id or Element from which the message box should animate as it opens and
4914 closes (defaults to undefined)
4915 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4916 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4917 closable Boolean False to hide the top-right close button (defaults to true). Note that
4918 progress and wait dialogs will ignore this property and always hide the
4919 close button as they can only be closed programmatically.
4920 cls String A custom CSS class to apply to the message box element
4921 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4922 displayed (defaults to 75)
4923 fn Function A callback function to execute after closing the dialog. The arguments to the
4924 function will be btn (the name of the button that was clicked, if applicable,
4925 e.g. "ok"), and text (the value of the active text field, if applicable).
4926 Progress and wait dialogs will ignore this option since they do not respond to
4927 user actions and can only be closed programmatically, so any required function
4928 should be called by the same code after it closes the dialog.
4929 icon String A CSS class that provides a background image to be used as an icon for
4930 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4931 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4932 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4933 modal Boolean False to allow user interaction with the page while the message box is
4934 displayed (defaults to true)
4935 msg String A string that will replace the existing message box body text (defaults
4936 to the XHTML-compliant non-breaking space character ' ')
4937 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4938 progress Boolean True to display a progress bar (defaults to false)
4939 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4940 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4941 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4942 title String The title text
4943 value String The string value to set into the active textbox element if displayed
4944 wait Boolean True to display a progress bar (defaults to false)
4945 width Number The width of the dialog in pixels
4952 msg: 'Please enter your address:',
4954 buttons: Roo.MessageBox.OKCANCEL,
4957 animEl: 'addAddressBtn'
4960 * @param {Object} config Configuration options
4961 * @return {Roo.MessageBox} This message box
4963 show : function(options)
4966 // this causes nightmares if you show one dialog after another
4967 // especially on callbacks..
4969 if(this.isVisible()){
4972 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4973 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4974 Roo.log("New Dialog Message:" + options.msg )
4975 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4976 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4979 var d = this.getDialog();
4981 d.setTitle(opt.title || " ");
4982 d.closeEl.setDisplayed(opt.closable !== false);
4983 activeTextEl = textboxEl;
4984 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4989 textareaEl.setHeight(typeof opt.multiline == "number" ?
4990 opt.multiline : this.defaultTextHeight);
4991 activeTextEl = textareaEl;
5000 progressEl.setDisplayed(opt.progress === true);
5002 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5004 this.updateProgress(0);
5005 activeTextEl.dom.value = opt.value || "";
5007 dlg.setDefaultButton(activeTextEl);
5009 var bs = opt.buttons;
5013 }else if(bs && bs.yes){
5014 db = buttons["yes"];
5016 dlg.setDefaultButton(db);
5018 bwidth = updateButtons(opt.buttons);
5019 this.updateText(opt.msg);
5021 d.el.addClass(opt.cls);
5023 d.proxyDrag = opt.proxyDrag === true;
5024 d.modal = opt.modal !== false;
5025 d.mask = opt.modal !== false ? mask : false;
5027 // force it to the end of the z-index stack so it gets a cursor in FF
5028 document.body.appendChild(dlg.el.dom);
5029 d.animateTarget = null;
5030 d.show(options.animEl);
5036 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5037 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5038 * and closing the message box when the process is complete.
5039 * @param {String} title The title bar text
5040 * @param {String} msg The message box body text
5041 * @return {Roo.MessageBox} This message box
5043 progress : function(title, msg){
5050 minWidth: this.minProgressWidth,
5057 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5058 * If a callback function is passed it will be called after the user clicks the button, and the
5059 * id of the button that was clicked will be passed as the only parameter to the callback
5060 * (could also be the top-right close button).
5061 * @param {String} title The title bar text
5062 * @param {String} msg The message box body text
5063 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5064 * @param {Object} scope (optional) The scope of the callback function
5065 * @return {Roo.MessageBox} This message box
5067 alert : function(title, msg, fn, scope)
5082 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5083 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5084 * You are responsible for closing the message box when the process is complete.
5085 * @param {String} msg The message box body text
5086 * @param {String} title (optional) The title bar text
5087 * @return {Roo.MessageBox} This message box
5089 wait : function(msg, title){
5100 waitTimer = Roo.TaskMgr.start({
5102 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5110 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5111 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5112 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5113 * @param {String} title The title bar text
5114 * @param {String} msg The message box body text
5115 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5116 * @param {Object} scope (optional) The scope of the callback function
5117 * @return {Roo.MessageBox} This message box
5119 confirm : function(title, msg, fn, scope){
5123 buttons: this.YESNO,
5132 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5133 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5134 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5135 * (could also be the top-right close button) and the text that was entered will be passed as the two
5136 * parameters to the callback.
5137 * @param {String} title The title bar text
5138 * @param {String} msg The message box body text
5139 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5140 * @param {Object} scope (optional) The scope of the callback function
5141 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5142 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5143 * @return {Roo.MessageBox} This message box
5145 prompt : function(title, msg, fn, scope, multiline){
5149 buttons: this.OKCANCEL,
5154 multiline: multiline,
5161 * Button config that displays a single OK button
5166 * Button config that displays Yes and No buttons
5169 YESNO : {yes:true, no:true},
5171 * Button config that displays OK and Cancel buttons
5174 OKCANCEL : {ok:true, cancel:true},
5176 * Button config that displays Yes, No and Cancel buttons
5179 YESNOCANCEL : {yes:true, no:true, cancel:true},
5182 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5185 defaultTextHeight : 75,
5187 * The maximum width in pixels of the message box (defaults to 600)
5192 * The minimum width in pixels of the message box (defaults to 100)
5197 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5198 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5201 minProgressWidth : 250,
5203 * An object containing the default button text strings that can be overriden for localized language support.
5204 * Supported properties are: ok, cancel, yes and no.
5205 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5218 * Shorthand for {@link Roo.MessageBox}
5220 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5221 Roo.Msg = Roo.Msg || Roo.MessageBox;
5230 * @class Roo.bootstrap.Navbar
5231 * @extends Roo.bootstrap.Component
5232 * Bootstrap Navbar class
5235 * Create a new Navbar
5236 * @param {Object} config The config object
5240 Roo.bootstrap.Navbar = function(config){
5241 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5245 * @event beforetoggle
5246 * Fire before toggle the menu
5247 * @param {Roo.EventObject} e
5249 "beforetoggle" : true
5253 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5262 getAutoCreate : function(){
5265 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5269 initEvents :function ()
5271 //Roo.log(this.el.select('.navbar-toggle',true));
5272 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5279 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5281 var size = this.el.getSize();
5282 this.maskEl.setSize(size.width, size.height);
5283 this.maskEl.enableDisplayMode("block");
5292 getChildContainer : function()
5294 if (this.el && this.el.select('.collapse').getCount()) {
5295 return this.el.select('.collapse',true).first();
5310 onToggle : function()
5313 if(this.fireEvent('beforetoggle', this) === false){
5316 var ce = this.el.select('.navbar-collapse',true).first();
5318 if (!ce.hasClass('show')) {
5328 * Expand the navbar pulldown
5330 expand : function ()
5333 var ce = this.el.select('.navbar-collapse',true).first();
5334 if (ce.hasClass('collapsing')) {
5337 ce.dom.style.height = '';
5339 ce.addClass('in'); // old...
5340 ce.removeClass('collapse');
5341 ce.addClass('show');
5342 var h = ce.getHeight();
5344 ce.removeClass('show');
5345 // at this point we should be able to see it..
5346 ce.addClass('collapsing');
5348 ce.setHeight(0); // resize it ...
5349 ce.on('transitionend', function() {
5350 //Roo.log('done transition');
5351 ce.removeClass('collapsing');
5352 ce.addClass('show');
5353 ce.removeClass('collapse');
5355 ce.dom.style.height = '';
5356 }, this, { single: true} );
5358 ce.dom.scrollTop = 0;
5361 * Collapse the navbar pulldown
5363 collapse : function()
5365 var ce = this.el.select('.navbar-collapse',true).first();
5367 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5368 // it's collapsed or collapsing..
5371 ce.removeClass('in'); // old...
5372 ce.setHeight(ce.getHeight());
5373 ce.removeClass('show');
5374 ce.addClass('collapsing');
5376 ce.on('transitionend', function() {
5377 ce.dom.style.height = '';
5378 ce.removeClass('collapsing');
5379 ce.addClass('collapse');
5380 }, this, { single: true} );
5400 * @class Roo.bootstrap.NavSimplebar
5401 * @extends Roo.bootstrap.Navbar
5402 * Bootstrap Sidebar class
5404 * @cfg {Boolean} inverse is inverted color
5406 * @cfg {String} type (nav | pills | tabs)
5407 * @cfg {Boolean} arrangement stacked | justified
5408 * @cfg {String} align (left | right) alignment
5410 * @cfg {Boolean} main (true|false) main nav bar? default false
5411 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5413 * @cfg {String} tag (header|footer|nav|div) default is nav
5415 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5419 * Create a new Sidebar
5420 * @param {Object} config The config object
5424 Roo.bootstrap.NavSimplebar = function(config){
5425 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5428 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5444 getAutoCreate : function(){
5448 tag : this.tag || 'div',
5449 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5451 if (['light','white'].indexOf(this.weight) > -1) {
5452 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5454 cfg.cls += ' bg-' + this.weight;
5457 cfg.cls += ' navbar-inverse';
5461 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5463 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5472 cls: 'nav nav-' + this.xtype,
5478 this.type = this.type || 'nav';
5479 if (['tabs','pills'].indexOf(this.type) != -1) {
5480 cfg.cn[0].cls += ' nav-' + this.type
5484 if (this.type!=='nav') {
5485 Roo.log('nav type must be nav/tabs/pills')
5487 cfg.cn[0].cls += ' navbar-nav'
5493 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5494 cfg.cn[0].cls += ' nav-' + this.arrangement;
5498 if (this.align === 'right') {
5499 cfg.cn[0].cls += ' navbar-right';
5524 * navbar-expand-md fixed-top
5528 * @class Roo.bootstrap.NavHeaderbar
5529 * @extends Roo.bootstrap.NavSimplebar
5530 * Bootstrap Sidebar class
5532 * @cfg {String} brand what is brand
5533 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5534 * @cfg {String} brand_href href of the brand
5535 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5536 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5537 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5538 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5541 * Create a new Sidebar
5542 * @param {Object} config The config object
5546 Roo.bootstrap.NavHeaderbar = function(config){
5547 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5551 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5558 desktopCenter : false,
5561 getAutoCreate : function(){
5564 tag: this.nav || 'nav',
5565 cls: 'navbar navbar-expand-md',
5571 if (this.desktopCenter) {
5572 cn.push({cls : 'container', cn : []});
5580 cls: 'navbar-toggle navbar-toggler',
5581 'data-toggle': 'collapse',
5586 html: 'Toggle navigation'
5590 cls: 'icon-bar navbar-toggler-icon'
5603 cn.push( Roo.bootstrap.version == 4 ? btn : {
5605 cls: 'navbar-header',
5614 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5618 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5620 if (['light','white'].indexOf(this.weight) > -1) {
5621 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5623 cfg.cls += ' bg-' + this.weight;
5626 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5627 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5629 // tag can override this..
5631 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5634 if (this.brand !== '') {
5635 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5636 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5638 href: this.brand_href ? this.brand_href : '#',
5639 cls: 'navbar-brand',
5647 cfg.cls += ' main-nav';
5655 getHeaderChildContainer : function()
5657 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5658 return this.el.select('.navbar-header',true).first();
5661 return this.getChildContainer();
5664 getChildContainer : function()
5667 return this.el.select('.roo-navbar-collapse',true).first();
5672 initEvents : function()
5674 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5676 if (this.autohide) {
5681 Roo.get(document).on('scroll',function(e) {
5682 var ns = Roo.get(document).getScroll().top;
5683 var os = prevScroll;
5687 ft.removeClass('slideDown');
5688 ft.addClass('slideUp');
5691 ft.removeClass('slideUp');
5692 ft.addClass('slideDown');
5713 * @class Roo.bootstrap.NavSidebar
5714 * @extends Roo.bootstrap.Navbar
5715 * Bootstrap Sidebar class
5718 * Create a new Sidebar
5719 * @param {Object} config The config object
5723 Roo.bootstrap.NavSidebar = function(config){
5724 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5727 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5729 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5731 getAutoCreate : function(){
5736 cls: 'sidebar sidebar-nav'
5758 * @class Roo.bootstrap.NavGroup
5759 * @extends Roo.bootstrap.Component
5760 * Bootstrap NavGroup class
5761 * @cfg {String} align (left|right)
5762 * @cfg {Boolean} inverse
5763 * @cfg {String} type (nav|pills|tab) default nav
5764 * @cfg {String} navId - reference Id for navbar.
5765 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5768 * Create a new nav group
5769 * @param {Object} config The config object
5772 Roo.bootstrap.NavGroup = function(config){
5773 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5776 Roo.bootstrap.NavGroup.register(this);
5780 * Fires when the active item changes
5781 * @param {Roo.bootstrap.NavGroup} this
5782 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5783 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5790 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5802 getAutoCreate : function()
5804 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5810 if (Roo.bootstrap.version == 4) {
5811 if (['tabs','pills'].indexOf(this.type) != -1) {
5812 cfg.cls += ' nav-' + this.type;
5814 // trying to remove so header bar can right align top?
5815 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5816 // do not use on header bar...
5817 cfg.cls += ' navbar-nav';
5822 if (['tabs','pills'].indexOf(this.type) != -1) {
5823 cfg.cls += ' nav-' + this.type
5825 if (this.type !== 'nav') {
5826 Roo.log('nav type must be nav/tabs/pills')
5828 cfg.cls += ' navbar-nav'
5832 if (this.parent() && this.parent().sidebar) {
5835 cls: 'dashboard-menu sidebar-menu'
5841 if (this.form === true) {
5844 cls: 'navbar-form form-inline'
5846 //nav navbar-right ml-md-auto
5847 if (this.align === 'right') {
5848 cfg.cls += ' navbar-right ml-md-auto';
5850 cfg.cls += ' navbar-left';
5854 if (this.align === 'right') {
5855 cfg.cls += ' navbar-right ml-md-auto';
5857 cfg.cls += ' mr-auto';
5861 cfg.cls += ' navbar-inverse';
5869 * sets the active Navigation item
5870 * @param {Roo.bootstrap.NavItem} the new current navitem
5872 setActiveItem : function(item)
5875 Roo.each(this.navItems, function(v){
5880 v.setActive(false, true);
5887 item.setActive(true, true);
5888 this.fireEvent('changed', this, item, prev);
5893 * gets the active Navigation item
5894 * @return {Roo.bootstrap.NavItem} the current navitem
5896 getActive : function()
5900 Roo.each(this.navItems, function(v){
5911 indexOfNav : function()
5915 Roo.each(this.navItems, function(v,i){
5926 * adds a Navigation item
5927 * @param {Roo.bootstrap.NavItem} the navitem to add
5929 addItem : function(cfg)
5931 if (this.form && Roo.bootstrap.version == 4) {
5934 var cn = new Roo.bootstrap.NavItem(cfg);
5936 cn.parentId = this.id;
5937 cn.onRender(this.el, null);
5941 * register a Navigation item
5942 * @param {Roo.bootstrap.NavItem} the navitem to add
5944 register : function(item)
5946 this.navItems.push( item);
5947 item.navId = this.navId;
5952 * clear all the Navigation item
5955 clearAll : function()
5958 this.el.dom.innerHTML = '';
5961 getNavItem: function(tabId)
5964 Roo.each(this.navItems, function(e) {
5965 if (e.tabId == tabId) {
5975 setActiveNext : function()
5977 var i = this.indexOfNav(this.getActive());
5978 if (i > this.navItems.length) {
5981 this.setActiveItem(this.navItems[i+1]);
5983 setActivePrev : function()
5985 var i = this.indexOfNav(this.getActive());
5989 this.setActiveItem(this.navItems[i-1]);
5991 clearWasActive : function(except) {
5992 Roo.each(this.navItems, function(e) {
5993 if (e.tabId != except.tabId && e.was_active) {
5994 e.was_active = false;
6001 getWasActive : function ()
6004 Roo.each(this.navItems, function(e) {
6019 Roo.apply(Roo.bootstrap.NavGroup, {
6023 * register a Navigation Group
6024 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6026 register : function(navgrp)
6028 this.groups[navgrp.navId] = navgrp;
6032 * fetch a Navigation Group based on the navigation ID
6033 * @param {string} the navgroup to add
6034 * @returns {Roo.bootstrap.NavGroup} the navgroup
6036 get: function(navId) {
6037 if (typeof(this.groups[navId]) == 'undefined') {
6039 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6041 return this.groups[navId] ;
6056 * @class Roo.bootstrap.NavItem
6057 * @extends Roo.bootstrap.Component
6058 * Bootstrap Navbar.NavItem class
6059 * @cfg {String} href link to
6060 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6061 * @cfg {Boolean} button_outline show and outlined button
6062 * @cfg {String} html content of button
6063 * @cfg {String} badge text inside badge
6064 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6065 * @cfg {String} glyphicon DEPRICATED - use fa
6066 * @cfg {String} icon DEPRICATED - use fa
6067 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6068 * @cfg {Boolean} active Is item active
6069 * @cfg {Boolean} disabled Is item disabled
6070 * @cfg {String} linkcls Link Class
6071 * @cfg {Boolean} preventDefault (true | false) default false
6072 * @cfg {String} tabId the tab that this item activates.
6073 * @cfg {String} tagtype (a|span) render as a href or span?
6074 * @cfg {Boolean} animateRef (true|false) link to element default false
6077 * Create a new Navbar Item
6078 * @param {Object} config The config object
6080 Roo.bootstrap.NavItem = function(config){
6081 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6086 * The raw click event for the entire grid.
6087 * @param {Roo.EventObject} e
6092 * Fires when the active item active state changes
6093 * @param {Roo.bootstrap.NavItem} this
6094 * @param {boolean} state the new state
6100 * Fires when scroll to element
6101 * @param {Roo.bootstrap.NavItem} this
6102 * @param {Object} options
6103 * @param {Roo.EventObject} e
6111 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6120 preventDefault : false,
6128 button_outline : false,
6132 getAutoCreate : function(){
6139 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6142 cfg.cls += ' active' ;
6144 if (this.disabled) {
6145 cfg.cls += ' disabled';
6149 if (this.button_weight.length) {
6150 cfg.tag = this.href ? 'a' : 'button';
6151 cfg.html = this.html || '';
6152 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6154 cfg.href = this.href;
6157 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6160 // menu .. should add dropdown-menu class - so no need for carat..
6162 if (this.badge !== '') {
6164 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6169 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6173 href : this.href || "#",
6174 html: this.html || ''
6177 if (this.tagtype == 'a') {
6178 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6182 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6185 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6187 if(this.glyphicon) {
6188 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6193 cfg.cn[0].html += " <span class='caret'></span>";
6197 if (this.badge !== '') {
6199 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6207 onRender : function(ct, position)
6209 // Roo.log("Call onRender: " + this.xtype);
6210 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6214 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6215 this.navLink = this.el.select('.nav-link',true).first();
6220 initEvents: function()
6222 if (typeof (this.menu) != 'undefined') {
6223 this.menu.parentType = this.xtype;
6224 this.menu.triggerEl = this.el;
6225 this.menu = this.addxtype(Roo.apply({}, this.menu));
6228 this.el.on('click', this.onClick, this);
6230 //if(this.tagtype == 'span'){
6231 // this.el.select('span',true).on('click', this.onClick, this);
6234 // at this point parent should be available..
6235 this.parent().register(this);
6238 onClick : function(e)
6240 if (e.getTarget('.dropdown-menu-item')) {
6241 // did you click on a menu itemm.... - then don't trigger onclick..
6246 this.preventDefault ||
6249 Roo.log("NavItem - prevent Default?");
6253 if (this.disabled) {
6257 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6258 if (tg && tg.transition) {
6259 Roo.log("waiting for the transitionend");
6265 //Roo.log("fire event clicked");
6266 if(this.fireEvent('click', this, e) === false){
6270 if(this.tagtype == 'span'){
6274 //Roo.log(this.href);
6275 var ael = this.el.select('a',true).first();
6278 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6279 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6280 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6281 return; // ignore... - it's a 'hash' to another page.
6283 Roo.log("NavItem - prevent Default?");
6285 this.scrollToElement(e);
6289 var p = this.parent();
6291 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6292 if (typeof(p.setActiveItem) !== 'undefined') {
6293 p.setActiveItem(this);
6297 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6298 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6299 // remove the collapsed menu expand...
6300 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6304 isActive: function () {
6307 setActive : function(state, fire, is_was_active)
6309 if (this.active && !state && this.navId) {
6310 this.was_active = true;
6311 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6313 nv.clearWasActive(this);
6317 this.active = state;
6320 this.el.removeClass('active');
6321 this.navLink ? this.navLink.removeClass('active') : false;
6322 } else if (!this.el.hasClass('active')) {
6324 this.el.addClass('active');
6325 if (Roo.bootstrap.version == 4 && this.navLink ) {
6326 this.navLink.addClass('active');
6331 this.fireEvent('changed', this, state);
6334 // show a panel if it's registered and related..
6336 if (!this.navId || !this.tabId || !state || is_was_active) {
6340 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6344 var pan = tg.getPanelByName(this.tabId);
6348 // if we can not flip to new panel - go back to old nav highlight..
6349 if (false == tg.showPanel(pan)) {
6350 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6352 var onav = nv.getWasActive();
6354 onav.setActive(true, false, true);
6363 // this should not be here...
6364 setDisabled : function(state)
6366 this.disabled = state;
6368 this.el.removeClass('disabled');
6369 } else if (!this.el.hasClass('disabled')) {
6370 this.el.addClass('disabled');
6376 * Fetch the element to display the tooltip on.
6377 * @return {Roo.Element} defaults to this.el
6379 tooltipEl : function()
6381 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6384 scrollToElement : function(e)
6386 var c = document.body;
6389 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6391 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6392 c = document.documentElement;
6395 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6401 var o = target.calcOffsetsTo(c);
6408 this.fireEvent('scrollto', this, options, e);
6410 Roo.get(c).scrollTo('top', options.value, true);
6423 * <span> icon </span>
6424 * <span> text </span>
6425 * <span>badge </span>
6429 * @class Roo.bootstrap.NavSidebarItem
6430 * @extends Roo.bootstrap.NavItem
6431 * Bootstrap Navbar.NavSidebarItem class
6432 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6433 * {Boolean} open is the menu open
6434 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6435 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6436 * {String} buttonSize (sm|md|lg)the extra classes for the button
6437 * {Boolean} showArrow show arrow next to the text (default true)
6439 * Create a new Navbar Button
6440 * @param {Object} config The config object
6442 Roo.bootstrap.NavSidebarItem = function(config){
6443 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6448 * The raw click event for the entire grid.
6449 * @param {Roo.EventObject} e
6454 * Fires when the active item active state changes
6455 * @param {Roo.bootstrap.NavSidebarItem} this
6456 * @param {boolean} state the new state
6464 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6466 badgeWeight : 'default',
6472 buttonWeight : 'default',
6478 getAutoCreate : function(){
6483 href : this.href || '#',
6489 if(this.buttonView){
6492 href : this.href || '#',
6493 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6506 cfg.cls += ' active';
6509 if (this.disabled) {
6510 cfg.cls += ' disabled';
6513 cfg.cls += ' open x-open';
6516 if (this.glyphicon || this.icon) {
6517 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6518 a.cn.push({ tag : 'i', cls : c }) ;
6521 if(!this.buttonView){
6524 html : this.html || ''
6531 if (this.badge !== '') {
6532 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6538 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6541 a.cls += ' dropdown-toggle treeview' ;
6547 initEvents : function()
6549 if (typeof (this.menu) != 'undefined') {
6550 this.menu.parentType = this.xtype;
6551 this.menu.triggerEl = this.el;
6552 this.menu = this.addxtype(Roo.apply({}, this.menu));
6555 this.el.on('click', this.onClick, this);
6557 if(this.badge !== ''){
6558 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6563 onClick : function(e)
6570 if(this.preventDefault){
6574 this.fireEvent('click', this, e);
6577 disable : function()
6579 this.setDisabled(true);
6584 this.setDisabled(false);
6587 setDisabled : function(state)
6589 if(this.disabled == state){
6593 this.disabled = state;
6596 this.el.addClass('disabled');
6600 this.el.removeClass('disabled');
6605 setActive : function(state)
6607 if(this.active == state){
6611 this.active = state;
6614 this.el.addClass('active');
6618 this.el.removeClass('active');
6623 isActive: function ()
6628 setBadge : function(str)
6634 this.badgeEl.dom.innerHTML = str;
6649 Roo.namespace('Roo.bootstrap.breadcrumb');
6653 * @class Roo.bootstrap.breadcrumb.Nav
6654 * @extends Roo.bootstrap.Component
6655 * Bootstrap Breadcrumb Nav Class
6657 * @children Roo.bootstrap.breadcrumb.Item
6660 * Create a new breadcrumb.Nav
6661 * @param {Object} config The config object
6665 Roo.bootstrap.breadcrumb.Nav = function(config){
6666 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6671 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6673 getAutoCreate : function()
6690 initEvents: function()
6692 this.olEl = this.el.select('ol',true).first();
6694 getChildContainer : function()
6710 * @class Roo.bootstrap.breadcrumb.Nav
6711 * @extends Roo.bootstrap.Component
6712 * Bootstrap Breadcrumb Nav Class
6714 * @children Roo.bootstrap.breadcrumb.Component
6715 * @cfg {String} html the content of the link.
6716 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6717 * @cfg {Boolean} active is it active
6721 * Create a new breadcrumb.Nav
6722 * @param {Object} config The config object
6725 Roo.bootstrap.breadcrumb.Item = function(config){
6726 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6731 * The img click event for the img.
6732 * @param {Roo.EventObject} e
6739 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6744 getAutoCreate : function()
6749 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6751 if (this.href !== false) {
6758 cfg.html = this.html;
6764 initEvents: function()
6767 this.el.select('a', true).first().on('click',this.onClick, this)
6771 onClick : function(e)
6774 this.fireEvent('click',this, e);
6787 * @class Roo.bootstrap.Row
6788 * @extends Roo.bootstrap.Component
6789 * Bootstrap Row class (contains columns...)
6793 * @param {Object} config The config object
6796 Roo.bootstrap.Row = function(config){
6797 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6800 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6802 getAutoCreate : function(){
6821 * @class Roo.bootstrap.Pagination
6822 * @extends Roo.bootstrap.Component
6823 * Bootstrap Pagination class
6824 * @cfg {String} size xs | sm | md | lg
6825 * @cfg {Boolean} inverse false | true
6828 * Create a new Pagination
6829 * @param {Object} config The config object
6832 Roo.bootstrap.Pagination = function(config){
6833 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6836 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6842 getAutoCreate : function(){
6848 cfg.cls += ' inverse';
6854 cfg.cls += " " + this.cls;
6872 * @class Roo.bootstrap.PaginationItem
6873 * @extends Roo.bootstrap.Component
6874 * Bootstrap PaginationItem class
6875 * @cfg {String} html text
6876 * @cfg {String} href the link
6877 * @cfg {Boolean} preventDefault (true | false) default true
6878 * @cfg {Boolean} active (true | false) default false
6879 * @cfg {Boolean} disabled default false
6883 * Create a new PaginationItem
6884 * @param {Object} config The config object
6888 Roo.bootstrap.PaginationItem = function(config){
6889 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6894 * The raw click event for the entire grid.
6895 * @param {Roo.EventObject} e
6901 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6905 preventDefault: true,
6910 getAutoCreate : function(){
6916 href : this.href ? this.href : '#',
6917 html : this.html ? this.html : ''
6927 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6931 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6937 initEvents: function() {
6939 this.el.on('click', this.onClick, this);
6942 onClick : function(e)
6944 Roo.log('PaginationItem on click ');
6945 if(this.preventDefault){
6953 this.fireEvent('click', this, e);
6969 * @class Roo.bootstrap.Slider
6970 * @extends Roo.bootstrap.Component
6971 * Bootstrap Slider class
6974 * Create a new Slider
6975 * @param {Object} config The config object
6978 Roo.bootstrap.Slider = function(config){
6979 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6982 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6984 getAutoCreate : function(){
6988 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6992 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7004 * Ext JS Library 1.1.1
7005 * Copyright(c) 2006-2007, Ext JS, LLC.
7007 * Originally Released Under LGPL - original licence link has changed is not relivant.
7010 * <script type="text/javascript">
7015 * @class Roo.grid.ColumnModel
7016 * @extends Roo.util.Observable
7017 * This is the default implementation of a ColumnModel used by the Grid. It defines
7018 * the columns in the grid.
7021 var colModel = new Roo.grid.ColumnModel([
7022 {header: "Ticker", width: 60, sortable: true, locked: true},
7023 {header: "Company Name", width: 150, sortable: true},
7024 {header: "Market Cap.", width: 100, sortable: true},
7025 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7026 {header: "Employees", width: 100, sortable: true, resizable: false}
7031 * The config options listed for this class are options which may appear in each
7032 * individual column definition.
7033 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7035 * @param {Object} config An Array of column config objects. See this class's
7036 * config objects for details.
7038 Roo.grid.ColumnModel = function(config){
7040 * The config passed into the constructor
7042 this.config = config;
7045 // if no id, create one
7046 // if the column does not have a dataIndex mapping,
7047 // map it to the order it is in the config
7048 for(var i = 0, len = config.length; i < len; i++){
7050 if(typeof c.dataIndex == "undefined"){
7053 if(typeof c.renderer == "string"){
7054 c.renderer = Roo.util.Format[c.renderer];
7056 if(typeof c.id == "undefined"){
7059 if(c.editor && c.editor.xtype){
7060 c.editor = Roo.factory(c.editor, Roo.grid);
7062 if(c.editor && c.editor.isFormField){
7063 c.editor = new Roo.grid.GridEditor(c.editor);
7065 this.lookup[c.id] = c;
7069 * The width of columns which have no width specified (defaults to 100)
7072 this.defaultWidth = 100;
7075 * Default sortable of columns which have no sortable specified (defaults to false)
7078 this.defaultSortable = false;
7082 * @event widthchange
7083 * Fires when the width of a column changes.
7084 * @param {ColumnModel} this
7085 * @param {Number} columnIndex The column index
7086 * @param {Number} newWidth The new width
7088 "widthchange": true,
7090 * @event headerchange
7091 * Fires when the text of a header changes.
7092 * @param {ColumnModel} this
7093 * @param {Number} columnIndex The column index
7094 * @param {Number} newText The new header text
7096 "headerchange": true,
7098 * @event hiddenchange
7099 * Fires when a column is hidden or "unhidden".
7100 * @param {ColumnModel} this
7101 * @param {Number} columnIndex The column index
7102 * @param {Boolean} hidden true if hidden, false otherwise
7104 "hiddenchange": true,
7106 * @event columnmoved
7107 * Fires when a column is moved.
7108 * @param {ColumnModel} this
7109 * @param {Number} oldIndex
7110 * @param {Number} newIndex
7112 "columnmoved" : true,
7114 * @event columlockchange
7115 * Fires when a column's locked state is changed
7116 * @param {ColumnModel} this
7117 * @param {Number} colIndex
7118 * @param {Boolean} locked true if locked
7120 "columnlockchange" : true
7122 Roo.grid.ColumnModel.superclass.constructor.call(this);
7124 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7126 * @cfg {String} header The header text to display in the Grid view.
7129 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7130 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7131 * specified, the column's index is used as an index into the Record's data Array.
7134 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7135 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7138 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7139 * Defaults to the value of the {@link #defaultSortable} property.
7140 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7143 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7146 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7149 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7152 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7155 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7156 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7157 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7158 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7161 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7164 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7167 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7170 * @cfg {String} cursor (Optional)
7173 * @cfg {String} tooltip (Optional)
7176 * @cfg {Number} xs (Optional)
7179 * @cfg {Number} sm (Optional)
7182 * @cfg {Number} md (Optional)
7185 * @cfg {Number} lg (Optional)
7188 * Returns the id of the column at the specified index.
7189 * @param {Number} index The column index
7190 * @return {String} the id
7192 getColumnId : function(index){
7193 return this.config[index].id;
7197 * Returns the column for a specified id.
7198 * @param {String} id The column id
7199 * @return {Object} the column
7201 getColumnById : function(id){
7202 return this.lookup[id];
7207 * Returns the column for a specified dataIndex.
7208 * @param {String} dataIndex The column dataIndex
7209 * @return {Object|Boolean} the column or false if not found
7211 getColumnByDataIndex: function(dataIndex){
7212 var index = this.findColumnIndex(dataIndex);
7213 return index > -1 ? this.config[index] : false;
7217 * Returns the index for a specified column id.
7218 * @param {String} id The column id
7219 * @return {Number} the index, or -1 if not found
7221 getIndexById : function(id){
7222 for(var i = 0, len = this.config.length; i < len; i++){
7223 if(this.config[i].id == id){
7231 * Returns the index for a specified column dataIndex.
7232 * @param {String} dataIndex The column dataIndex
7233 * @return {Number} the index, or -1 if not found
7236 findColumnIndex : function(dataIndex){
7237 for(var i = 0, len = this.config.length; i < len; i++){
7238 if(this.config[i].dataIndex == dataIndex){
7246 moveColumn : function(oldIndex, newIndex){
7247 var c = this.config[oldIndex];
7248 this.config.splice(oldIndex, 1);
7249 this.config.splice(newIndex, 0, c);
7250 this.dataMap = null;
7251 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7254 isLocked : function(colIndex){
7255 return this.config[colIndex].locked === true;
7258 setLocked : function(colIndex, value, suppressEvent){
7259 if(this.isLocked(colIndex) == value){
7262 this.config[colIndex].locked = value;
7264 this.fireEvent("columnlockchange", this, colIndex, value);
7268 getTotalLockedWidth : function(){
7270 for(var i = 0; i < this.config.length; i++){
7271 if(this.isLocked(i) && !this.isHidden(i)){
7272 this.totalWidth += this.getColumnWidth(i);
7278 getLockedCount : function(){
7279 for(var i = 0, len = this.config.length; i < len; i++){
7280 if(!this.isLocked(i)){
7285 return this.config.length;
7289 * Returns the number of columns.
7292 getColumnCount : function(visibleOnly){
7293 if(visibleOnly === true){
7295 for(var i = 0, len = this.config.length; i < len; i++){
7296 if(!this.isHidden(i)){
7302 return this.config.length;
7306 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7307 * @param {Function} fn
7308 * @param {Object} scope (optional)
7309 * @return {Array} result
7311 getColumnsBy : function(fn, scope){
7313 for(var i = 0, len = this.config.length; i < len; i++){
7314 var c = this.config[i];
7315 if(fn.call(scope||this, c, i) === true){
7323 * Returns true if the specified column is sortable.
7324 * @param {Number} col The column index
7327 isSortable : function(col){
7328 if(typeof this.config[col].sortable == "undefined"){
7329 return this.defaultSortable;
7331 return this.config[col].sortable;
7335 * Returns the rendering (formatting) function defined for the column.
7336 * @param {Number} col The column index.
7337 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7339 getRenderer : function(col){
7340 if(!this.config[col].renderer){
7341 return Roo.grid.ColumnModel.defaultRenderer;
7343 return this.config[col].renderer;
7347 * Sets the rendering (formatting) function for a column.
7348 * @param {Number} col The column index
7349 * @param {Function} fn The function to use to process the cell's raw data
7350 * to return HTML markup for the grid view. The render function is called with
7351 * the following parameters:<ul>
7352 * <li>Data value.</li>
7353 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7354 * <li>css A CSS style string to apply to the table cell.</li>
7355 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7356 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7357 * <li>Row index</li>
7358 * <li>Column index</li>
7359 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7361 setRenderer : function(col, fn){
7362 this.config[col].renderer = fn;
7366 * Returns the width for the specified column.
7367 * @param {Number} col The column index
7370 getColumnWidth : function(col){
7371 return this.config[col].width * 1 || this.defaultWidth;
7375 * Sets the width for a column.
7376 * @param {Number} col The column index
7377 * @param {Number} width The new width
7379 setColumnWidth : function(col, width, suppressEvent){
7380 this.config[col].width = width;
7381 this.totalWidth = null;
7383 this.fireEvent("widthchange", this, col, width);
7388 * Returns the total width of all columns.
7389 * @param {Boolean} includeHidden True to include hidden column widths
7392 getTotalWidth : function(includeHidden){
7393 if(!this.totalWidth){
7394 this.totalWidth = 0;
7395 for(var i = 0, len = this.config.length; i < len; i++){
7396 if(includeHidden || !this.isHidden(i)){
7397 this.totalWidth += this.getColumnWidth(i);
7401 return this.totalWidth;
7405 * Returns the header for the specified column.
7406 * @param {Number} col The column index
7409 getColumnHeader : function(col){
7410 return this.config[col].header;
7414 * Sets the header for a column.
7415 * @param {Number} col The column index
7416 * @param {String} header The new header
7418 setColumnHeader : function(col, header){
7419 this.config[col].header = header;
7420 this.fireEvent("headerchange", this, col, header);
7424 * Returns the tooltip for the specified column.
7425 * @param {Number} col The column index
7428 getColumnTooltip : function(col){
7429 return this.config[col].tooltip;
7432 * Sets the tooltip for a column.
7433 * @param {Number} col The column index
7434 * @param {String} tooltip The new tooltip
7436 setColumnTooltip : function(col, tooltip){
7437 this.config[col].tooltip = tooltip;
7441 * Returns the dataIndex for the specified column.
7442 * @param {Number} col The column index
7445 getDataIndex : function(col){
7446 return this.config[col].dataIndex;
7450 * Sets the dataIndex for a column.
7451 * @param {Number} col The column index
7452 * @param {Number} dataIndex The new dataIndex
7454 setDataIndex : function(col, dataIndex){
7455 this.config[col].dataIndex = dataIndex;
7461 * Returns true if the cell is editable.
7462 * @param {Number} colIndex The column index
7463 * @param {Number} rowIndex The row index - this is nto actually used..?
7466 isCellEditable : function(colIndex, rowIndex){
7467 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7471 * Returns the editor defined for the cell/column.
7472 * return false or null to disable editing.
7473 * @param {Number} colIndex The column index
7474 * @param {Number} rowIndex The row index
7477 getCellEditor : function(colIndex, rowIndex){
7478 return this.config[colIndex].editor;
7482 * Sets if a column is editable.
7483 * @param {Number} col The column index
7484 * @param {Boolean} editable True if the column is editable
7486 setEditable : function(col, editable){
7487 this.config[col].editable = editable;
7492 * Returns true if the column is hidden.
7493 * @param {Number} colIndex The column index
7496 isHidden : function(colIndex){
7497 return this.config[colIndex].hidden;
7502 * Returns true if the column width cannot be changed
7504 isFixed : function(colIndex){
7505 return this.config[colIndex].fixed;
7509 * Returns true if the column can be resized
7512 isResizable : function(colIndex){
7513 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7516 * Sets if a column is hidden.
7517 * @param {Number} colIndex The column index
7518 * @param {Boolean} hidden True if the column is hidden
7520 setHidden : function(colIndex, hidden){
7521 this.config[colIndex].hidden = hidden;
7522 this.totalWidth = null;
7523 this.fireEvent("hiddenchange", this, colIndex, hidden);
7527 * Sets the editor for a column.
7528 * @param {Number} col The column index
7529 * @param {Object} editor The editor object
7531 setEditor : function(col, editor){
7532 this.config[col].editor = editor;
7536 Roo.grid.ColumnModel.defaultRenderer = function(value)
7538 if(typeof value == "object") {
7541 if(typeof value == "string" && value.length < 1){
7545 return String.format("{0}", value);
7548 // Alias for backwards compatibility
7549 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7552 * Ext JS Library 1.1.1
7553 * Copyright(c) 2006-2007, Ext JS, LLC.
7555 * Originally Released Under LGPL - original licence link has changed is not relivant.
7558 * <script type="text/javascript">
7562 * @class Roo.LoadMask
7563 * A simple utility class for generically masking elements while loading data. If the element being masked has
7564 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7565 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7566 * element's UpdateManager load indicator and will be destroyed after the initial load.
7568 * Create a new LoadMask
7569 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7570 * @param {Object} config The config object
7572 Roo.LoadMask = function(el, config){
7573 this.el = Roo.get(el);
7574 Roo.apply(this, config);
7576 this.store.on('beforeload', this.onBeforeLoad, this);
7577 this.store.on('load', this.onLoad, this);
7578 this.store.on('loadexception', this.onLoadException, this);
7579 this.removeMask = false;
7581 var um = this.el.getUpdateManager();
7582 um.showLoadIndicator = false; // disable the default indicator
7583 um.on('beforeupdate', this.onBeforeLoad, this);
7584 um.on('update', this.onLoad, this);
7585 um.on('failure', this.onLoad, this);
7586 this.removeMask = true;
7590 Roo.LoadMask.prototype = {
7592 * @cfg {Boolean} removeMask
7593 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7594 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7598 * The text to display in a centered loading message box (defaults to 'Loading...')
7602 * @cfg {String} msgCls
7603 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7605 msgCls : 'x-mask-loading',
7608 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7614 * Disables the mask to prevent it from being displayed
7616 disable : function(){
7617 this.disabled = true;
7621 * Enables the mask so that it can be displayed
7623 enable : function(){
7624 this.disabled = false;
7627 onLoadException : function()
7631 if (typeof(arguments[3]) != 'undefined') {
7632 Roo.MessageBox.alert("Error loading",arguments[3]);
7636 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7637 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7644 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7649 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7653 onBeforeLoad : function(){
7655 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7660 destroy : function(){
7662 this.store.un('beforeload', this.onBeforeLoad, this);
7663 this.store.un('load', this.onLoad, this);
7664 this.store.un('loadexception', this.onLoadException, this);
7666 var um = this.el.getUpdateManager();
7667 um.un('beforeupdate', this.onBeforeLoad, this);
7668 um.un('update', this.onLoad, this);
7669 um.un('failure', this.onLoad, this);
7680 * @class Roo.bootstrap.Table
7681 * @extends Roo.bootstrap.Component
7682 * Bootstrap Table class
7683 * @cfg {String} cls table class
7684 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7685 * @cfg {String} bgcolor Specifies the background color for a table
7686 * @cfg {Number} border Specifies whether the table cells should have borders or not
7687 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7688 * @cfg {Number} cellspacing Specifies the space between cells
7689 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7690 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7691 * @cfg {String} sortable Specifies that the table should be sortable
7692 * @cfg {String} summary Specifies a summary of the content of a table
7693 * @cfg {Number} width Specifies the width of a table
7694 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7696 * @cfg {boolean} striped Should the rows be alternative striped
7697 * @cfg {boolean} bordered Add borders to the table
7698 * @cfg {boolean} hover Add hover highlighting
7699 * @cfg {boolean} condensed Format condensed
7700 * @cfg {boolean} responsive Format condensed
7701 * @cfg {Boolean} loadMask (true|false) default false
7702 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7703 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7704 * @cfg {Boolean} rowSelection (true|false) default false
7705 * @cfg {Boolean} cellSelection (true|false) default false
7706 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7707 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7708 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7709 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7713 * Create a new Table
7714 * @param {Object} config The config object
7717 Roo.bootstrap.Table = function(config){
7718 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7723 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7724 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7725 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7726 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7728 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7730 this.sm.grid = this;
7731 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7732 this.sm = this.selModel;
7733 this.sm.xmodule = this.xmodule || false;
7736 if (this.cm && typeof(this.cm.config) == 'undefined') {
7737 this.colModel = new Roo.grid.ColumnModel(this.cm);
7738 this.cm = this.colModel;
7739 this.cm.xmodule = this.xmodule || false;
7742 this.store= Roo.factory(this.store, Roo.data);
7743 this.ds = this.store;
7744 this.ds.xmodule = this.xmodule || false;
7747 if (this.footer && this.store) {
7748 this.footer.dataSource = this.ds;
7749 this.footer = Roo.factory(this.footer);
7756 * Fires when a cell is clicked
7757 * @param {Roo.bootstrap.Table} this
7758 * @param {Roo.Element} el
7759 * @param {Number} rowIndex
7760 * @param {Number} columnIndex
7761 * @param {Roo.EventObject} e
7765 * @event celldblclick
7766 * Fires when a cell is double clicked
7767 * @param {Roo.bootstrap.Table} this
7768 * @param {Roo.Element} el
7769 * @param {Number} rowIndex
7770 * @param {Number} columnIndex
7771 * @param {Roo.EventObject} e
7773 "celldblclick" : true,
7776 * Fires when a row is clicked
7777 * @param {Roo.bootstrap.Table} this
7778 * @param {Roo.Element} el
7779 * @param {Number} rowIndex
7780 * @param {Roo.EventObject} e
7784 * @event rowdblclick
7785 * Fires when a row is double clicked
7786 * @param {Roo.bootstrap.Table} this
7787 * @param {Roo.Element} el
7788 * @param {Number} rowIndex
7789 * @param {Roo.EventObject} e
7791 "rowdblclick" : true,
7794 * Fires when a mouseover occur
7795 * @param {Roo.bootstrap.Table} this
7796 * @param {Roo.Element} el
7797 * @param {Number} rowIndex
7798 * @param {Number} columnIndex
7799 * @param {Roo.EventObject} e
7804 * Fires when a mouseout occur
7805 * @param {Roo.bootstrap.Table} this
7806 * @param {Roo.Element} el
7807 * @param {Number} rowIndex
7808 * @param {Number} columnIndex
7809 * @param {Roo.EventObject} e
7814 * Fires when a row is rendered, so you can change add a style to it.
7815 * @param {Roo.bootstrap.Table} this
7816 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7820 * @event rowsrendered
7821 * Fires when all the rows have been rendered
7822 * @param {Roo.bootstrap.Table} this
7824 'rowsrendered' : true,
7826 * @event contextmenu
7827 * The raw contextmenu event for the entire grid.
7828 * @param {Roo.EventObject} e
7830 "contextmenu" : true,
7832 * @event rowcontextmenu
7833 * Fires when a row is right clicked
7834 * @param {Roo.bootstrap.Table} this
7835 * @param {Number} rowIndex
7836 * @param {Roo.EventObject} e
7838 "rowcontextmenu" : true,
7840 * @event cellcontextmenu
7841 * Fires when a cell is right clicked
7842 * @param {Roo.bootstrap.Table} this
7843 * @param {Number} rowIndex
7844 * @param {Number} cellIndex
7845 * @param {Roo.EventObject} e
7847 "cellcontextmenu" : true,
7849 * @event headercontextmenu
7850 * Fires when a header is right clicked
7851 * @param {Roo.bootstrap.Table} this
7852 * @param {Number} columnIndex
7853 * @param {Roo.EventObject} e
7855 "headercontextmenu" : true
7859 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7885 rowSelection : false,
7886 cellSelection : false,
7889 // Roo.Element - the tbody
7891 // Roo.Element - thead element
7894 container: false, // used by gridpanel...
7900 auto_hide_footer : false,
7902 getAutoCreate : function()
7904 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7911 if (this.scrollBody) {
7912 cfg.cls += ' table-body-fixed';
7915 cfg.cls += ' table-striped';
7919 cfg.cls += ' table-hover';
7921 if (this.bordered) {
7922 cfg.cls += ' table-bordered';
7924 if (this.condensed) {
7925 cfg.cls += ' table-condensed';
7927 if (this.responsive) {
7928 cfg.cls += ' table-responsive';
7932 cfg.cls+= ' ' +this.cls;
7935 // this lot should be simplifed...
7948 ].forEach(function(k) {
7956 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7959 if(this.store || this.cm){
7960 if(this.headerShow){
7961 cfg.cn.push(this.renderHeader());
7964 cfg.cn.push(this.renderBody());
7966 if(this.footerShow){
7967 cfg.cn.push(this.renderFooter());
7969 // where does this come from?
7970 //cfg.cls+= ' TableGrid';
7973 return { cn : [ cfg ] };
7976 initEvents : function()
7978 if(!this.store || !this.cm){
7981 if (this.selModel) {
7982 this.selModel.initEvents();
7986 //Roo.log('initEvents with ds!!!!');
7988 this.mainBody = this.el.select('tbody', true).first();
7989 this.mainHead = this.el.select('thead', true).first();
7990 this.mainFoot = this.el.select('tfoot', true).first();
7996 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7997 e.on('click', _this.sort, _this);
8000 this.mainBody.on("click", this.onClick, this);
8001 this.mainBody.on("dblclick", this.onDblClick, this);
8003 // why is this done????? = it breaks dialogs??
8004 //this.parent().el.setStyle('position', 'relative');
8008 this.footer.parentId = this.id;
8009 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8012 this.el.select('tfoot tr td').first().addClass('hide');
8017 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8020 this.store.on('load', this.onLoad, this);
8021 this.store.on('beforeload', this.onBeforeLoad, this);
8022 this.store.on('update', this.onUpdate, this);
8023 this.store.on('add', this.onAdd, this);
8024 this.store.on("clear", this.clear, this);
8026 this.el.on("contextmenu", this.onContextMenu, this);
8028 this.mainBody.on('scroll', this.onBodyScroll, this);
8030 this.cm.on("headerchange", this.onHeaderChange, this);
8032 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8036 onContextMenu : function(e, t)
8038 this.processEvent("contextmenu", e);
8041 processEvent : function(name, e)
8043 if (name != 'touchstart' ) {
8044 this.fireEvent(name, e);
8047 var t = e.getTarget();
8049 var cell = Roo.get(t);
8055 if(cell.findParent('tfoot', false, true)){
8059 if(cell.findParent('thead', false, true)){
8061 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8062 cell = Roo.get(t).findParent('th', false, true);
8064 Roo.log("failed to find th in thead?");
8065 Roo.log(e.getTarget());
8070 var cellIndex = cell.dom.cellIndex;
8072 var ename = name == 'touchstart' ? 'click' : name;
8073 this.fireEvent("header" + ename, this, cellIndex, e);
8078 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8079 cell = Roo.get(t).findParent('td', false, true);
8081 Roo.log("failed to find th in tbody?");
8082 Roo.log(e.getTarget());
8087 var row = cell.findParent('tr', false, true);
8088 var cellIndex = cell.dom.cellIndex;
8089 var rowIndex = row.dom.rowIndex - 1;
8093 this.fireEvent("row" + name, this, rowIndex, e);
8097 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8103 onMouseover : function(e, el)
8105 var cell = Roo.get(el);
8111 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8112 cell = cell.findParent('td', false, true);
8115 var row = cell.findParent('tr', false, true);
8116 var cellIndex = cell.dom.cellIndex;
8117 var rowIndex = row.dom.rowIndex - 1; // start from 0
8119 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8123 onMouseout : function(e, el)
8125 var cell = Roo.get(el);
8131 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8132 cell = cell.findParent('td', false, true);
8135 var row = cell.findParent('tr', false, true);
8136 var cellIndex = cell.dom.cellIndex;
8137 var rowIndex = row.dom.rowIndex - 1; // start from 0
8139 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8143 onClick : function(e, el)
8145 var cell = Roo.get(el);
8147 if(!cell || (!this.cellSelection && !this.rowSelection)){
8151 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8152 cell = cell.findParent('td', false, true);
8155 if(!cell || typeof(cell) == 'undefined'){
8159 var row = cell.findParent('tr', false, true);
8161 if(!row || typeof(row) == 'undefined'){
8165 var cellIndex = cell.dom.cellIndex;
8166 var rowIndex = this.getRowIndex(row);
8168 // why??? - should these not be based on SelectionModel?
8169 if(this.cellSelection){
8170 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8173 if(this.rowSelection){
8174 this.fireEvent('rowclick', this, row, rowIndex, e);
8180 onDblClick : function(e,el)
8182 var cell = Roo.get(el);
8184 if(!cell || (!this.cellSelection && !this.rowSelection)){
8188 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8189 cell = cell.findParent('td', false, true);
8192 if(!cell || typeof(cell) == 'undefined'){
8196 var row = cell.findParent('tr', false, true);
8198 if(!row || typeof(row) == 'undefined'){
8202 var cellIndex = cell.dom.cellIndex;
8203 var rowIndex = this.getRowIndex(row);
8205 if(this.cellSelection){
8206 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8209 if(this.rowSelection){
8210 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8214 sort : function(e,el)
8216 var col = Roo.get(el);
8218 if(!col.hasClass('sortable')){
8222 var sort = col.attr('sort');
8225 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8229 this.store.sortInfo = {field : sort, direction : dir};
8232 Roo.log("calling footer first");
8233 this.footer.onClick('first');
8236 this.store.load({ params : { start : 0 } });
8240 renderHeader : function()
8248 this.totalWidth = 0;
8250 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8252 var config = cm.config[i];
8256 cls : 'x-hcol-' + i,
8258 html: cm.getColumnHeader(i)
8263 if(typeof(config.sortable) != 'undefined' && config.sortable){
8265 c.html = '<i class="glyphicon"></i>' + c.html;
8268 // could use BS4 hidden-..-down
8270 if(typeof(config.lgHeader) != 'undefined'){
8271 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8274 if(typeof(config.mdHeader) != 'undefined'){
8275 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8278 if(typeof(config.smHeader) != 'undefined'){
8279 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8282 if(typeof(config.xsHeader) != 'undefined'){
8283 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8290 if(typeof(config.tooltip) != 'undefined'){
8291 c.tooltip = config.tooltip;
8294 if(typeof(config.colspan) != 'undefined'){
8295 c.colspan = config.colspan;
8298 if(typeof(config.hidden) != 'undefined' && config.hidden){
8299 c.style += ' display:none;';
8302 if(typeof(config.dataIndex) != 'undefined'){
8303 c.sort = config.dataIndex;
8308 if(typeof(config.align) != 'undefined' && config.align.length){
8309 c.style += ' text-align:' + config.align + ';';
8312 if(typeof(config.width) != 'undefined'){
8313 c.style += ' width:' + config.width + 'px;';
8314 this.totalWidth += config.width;
8316 this.totalWidth += 100; // assume minimum of 100 per column?
8319 if(typeof(config.cls) != 'undefined'){
8320 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8323 ['xs','sm','md','lg'].map(function(size){
8325 if(typeof(config[size]) == 'undefined'){
8329 if (!config[size]) { // 0 = hidden
8330 // BS 4 '0' is treated as hide that column and below.
8331 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8335 c.cls += ' col-' + size + '-' + config[size] + (
8336 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8348 renderBody : function()
8358 colspan : this.cm.getColumnCount()
8368 renderFooter : function()
8378 colspan : this.cm.getColumnCount()
8392 // Roo.log('ds onload');
8397 var ds = this.store;
8399 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8400 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8401 if (_this.store.sortInfo) {
8403 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8404 e.select('i', true).addClass(['glyphicon-arrow-up']);
8407 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8408 e.select('i', true).addClass(['glyphicon-arrow-down']);
8413 var tbody = this.mainBody;
8415 if(ds.getCount() > 0){
8416 ds.data.each(function(d,rowIndex){
8417 var row = this.renderRow(cm, ds, rowIndex);
8419 tbody.createChild(row);
8423 if(row.cellObjects.length){
8424 Roo.each(row.cellObjects, function(r){
8425 _this.renderCellObject(r);
8432 var tfoot = this.el.select('tfoot', true).first();
8434 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8436 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8438 var total = this.ds.getTotalCount();
8440 if(this.footer.pageSize < total){
8441 this.mainFoot.show();
8445 Roo.each(this.el.select('tbody td', true).elements, function(e){
8446 e.on('mouseover', _this.onMouseover, _this);
8449 Roo.each(this.el.select('tbody td', true).elements, function(e){
8450 e.on('mouseout', _this.onMouseout, _this);
8452 this.fireEvent('rowsrendered', this);
8458 onUpdate : function(ds,record)
8460 this.refreshRow(record);
8464 onRemove : function(ds, record, index, isUpdate){
8465 if(isUpdate !== true){
8466 this.fireEvent("beforerowremoved", this, index, record);
8468 var bt = this.mainBody.dom;
8470 var rows = this.el.select('tbody > tr', true).elements;
8472 if(typeof(rows[index]) != 'undefined'){
8473 bt.removeChild(rows[index].dom);
8476 // if(bt.rows[index]){
8477 // bt.removeChild(bt.rows[index]);
8480 if(isUpdate !== true){
8481 //this.stripeRows(index);
8482 //this.syncRowHeights(index, index);
8484 this.fireEvent("rowremoved", this, index, record);
8488 onAdd : function(ds, records, rowIndex)
8490 //Roo.log('on Add called');
8491 // - note this does not handle multiple adding very well..
8492 var bt = this.mainBody.dom;
8493 for (var i =0 ; i < records.length;i++) {
8494 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8495 //Roo.log(records[i]);
8496 //Roo.log(this.store.getAt(rowIndex+i));
8497 this.insertRow(this.store, rowIndex + i, false);
8504 refreshRow : function(record){
8505 var ds = this.store, index;
8506 if(typeof record == 'number'){
8508 record = ds.getAt(index);
8510 index = ds.indexOf(record);
8512 return; // should not happen - but seems to
8515 this.insertRow(ds, index, true);
8517 this.onRemove(ds, record, index+1, true);
8519 //this.syncRowHeights(index, index);
8521 this.fireEvent("rowupdated", this, index, record);
8524 insertRow : function(dm, rowIndex, isUpdate){
8527 this.fireEvent("beforerowsinserted", this, rowIndex);
8529 //var s = this.getScrollState();
8530 var row = this.renderRow(this.cm, this.store, rowIndex);
8531 // insert before rowIndex..
8532 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8536 if(row.cellObjects.length){
8537 Roo.each(row.cellObjects, function(r){
8538 _this.renderCellObject(r);
8543 this.fireEvent("rowsinserted", this, rowIndex);
8544 //this.syncRowHeights(firstRow, lastRow);
8545 //this.stripeRows(firstRow);
8552 getRowDom : function(rowIndex)
8554 var rows = this.el.select('tbody > tr', true).elements;
8556 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8559 // returns the object tree for a tr..
8562 renderRow : function(cm, ds, rowIndex)
8564 var d = ds.getAt(rowIndex);
8568 cls : 'x-row-' + rowIndex,
8572 var cellObjects = [];
8574 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8575 var config = cm.config[i];
8577 var renderer = cm.getRenderer(i);
8581 if(typeof(renderer) !== 'undefined'){
8582 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8584 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8585 // and are rendered into the cells after the row is rendered - using the id for the element.
8587 if(typeof(value) === 'object'){
8597 rowIndex : rowIndex,
8602 this.fireEvent('rowclass', this, rowcfg);
8606 cls : rowcfg.rowClass + ' x-col-' + i,
8608 html: (typeof(value) === 'object') ? '' : value
8615 if(typeof(config.colspan) != 'undefined'){
8616 td.colspan = config.colspan;
8619 if(typeof(config.hidden) != 'undefined' && config.hidden){
8620 td.style += ' display:none;';
8623 if(typeof(config.align) != 'undefined' && config.align.length){
8624 td.style += ' text-align:' + config.align + ';';
8626 if(typeof(config.valign) != 'undefined' && config.valign.length){
8627 td.style += ' vertical-align:' + config.valign + ';';
8630 if(typeof(config.width) != 'undefined'){
8631 td.style += ' width:' + config.width + 'px;';
8634 if(typeof(config.cursor) != 'undefined'){
8635 td.style += ' cursor:' + config.cursor + ';';
8638 if(typeof(config.cls) != 'undefined'){
8639 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8642 ['xs','sm','md','lg'].map(function(size){
8644 if(typeof(config[size]) == 'undefined'){
8650 if (!config[size]) { // 0 = hidden
8651 // BS 4 '0' is treated as hide that column and below.
8652 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8656 td.cls += ' col-' + size + '-' + config[size] + (
8657 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8667 row.cellObjects = cellObjects;
8675 onBeforeLoad : function()
8684 this.el.select('tbody', true).first().dom.innerHTML = '';
8687 * Show or hide a row.
8688 * @param {Number} rowIndex to show or hide
8689 * @param {Boolean} state hide
8691 setRowVisibility : function(rowIndex, state)
8693 var bt = this.mainBody.dom;
8695 var rows = this.el.select('tbody > tr', true).elements;
8697 if(typeof(rows[rowIndex]) == 'undefined'){
8700 rows[rowIndex].dom.style.display = state ? '' : 'none';
8704 getSelectionModel : function(){
8706 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8708 return this.selModel;
8711 * Render the Roo.bootstrap object from renderder
8713 renderCellObject : function(r)
8717 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8719 var t = r.cfg.render(r.container);
8722 Roo.each(r.cfg.cn, function(c){
8724 container: t.getChildContainer(),
8727 _this.renderCellObject(child);
8732 getRowIndex : function(row)
8736 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8747 * Returns the grid's underlying element = used by panel.Grid
8748 * @return {Element} The element
8750 getGridEl : function(){
8754 * Forces a resize - used by panel.Grid
8755 * @return {Element} The element
8757 autoSize : function()
8759 //var ctr = Roo.get(this.container.dom.parentElement);
8760 var ctr = Roo.get(this.el.dom);
8762 var thd = this.getGridEl().select('thead',true).first();
8763 var tbd = this.getGridEl().select('tbody', true).first();
8764 var tfd = this.getGridEl().select('tfoot', true).first();
8766 var cw = ctr.getWidth();
8767 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8771 tbd.setWidth(ctr.getWidth());
8772 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8773 // this needs fixing for various usage - currently only hydra job advers I think..
8775 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8777 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8780 cw = Math.max(cw, this.totalWidth);
8781 this.getGridEl().select('tbody tr',true).setWidth(cw);
8783 // resize 'expandable coloumn?
8785 return; // we doe not have a view in this design..
8788 onBodyScroll: function()
8790 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8792 this.mainHead.setStyle({
8793 'position' : 'relative',
8794 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8800 var scrollHeight = this.mainBody.dom.scrollHeight;
8802 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8804 var height = this.mainBody.getHeight();
8806 if(scrollHeight - height == scrollTop) {
8808 var total = this.ds.getTotalCount();
8810 if(this.footer.cursor + this.footer.pageSize < total){
8812 this.footer.ds.load({
8814 start : this.footer.cursor + this.footer.pageSize,
8815 limit : this.footer.pageSize
8825 onHeaderChange : function()
8827 var header = this.renderHeader();
8828 var table = this.el.select('table', true).first();
8830 this.mainHead.remove();
8831 this.mainHead = table.createChild(header, this.mainBody, false);
8834 onHiddenChange : function(colModel, colIndex, hidden)
8836 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8837 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8839 this.CSS.updateRule(thSelector, "display", "");
8840 this.CSS.updateRule(tdSelector, "display", "");
8843 this.CSS.updateRule(thSelector, "display", "none");
8844 this.CSS.updateRule(tdSelector, "display", "none");
8847 this.onHeaderChange();
8851 setColumnWidth: function(col_index, width)
8853 // width = "md-2 xs-2..."
8854 if(!this.colModel.config[col_index]) {
8858 var w = width.split(" ");
8860 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8862 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8865 for(var j = 0; j < w.length; j++) {
8871 var size_cls = w[j].split("-");
8873 if(!Number.isInteger(size_cls[1] * 1)) {
8877 if(!this.colModel.config[col_index][size_cls[0]]) {
8881 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8885 h_row[0].classList.replace(
8886 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8887 "col-"+size_cls[0]+"-"+size_cls[1]
8890 for(var i = 0; i < rows.length; i++) {
8892 var size_cls = w[j].split("-");
8894 if(!Number.isInteger(size_cls[1] * 1)) {
8898 if(!this.colModel.config[col_index][size_cls[0]]) {
8902 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8906 rows[i].classList.replace(
8907 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8908 "col-"+size_cls[0]+"-"+size_cls[1]
8912 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8927 * @class Roo.bootstrap.TableCell
8928 * @extends Roo.bootstrap.Component
8929 * Bootstrap TableCell class
8930 * @cfg {String} html cell contain text
8931 * @cfg {String} cls cell class
8932 * @cfg {String} tag cell tag (td|th) default td
8933 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8934 * @cfg {String} align Aligns the content in a cell
8935 * @cfg {String} axis Categorizes cells
8936 * @cfg {String} bgcolor Specifies the background color of a cell
8937 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8938 * @cfg {Number} colspan Specifies the number of columns a cell should span
8939 * @cfg {String} headers Specifies one or more header cells a cell is related to
8940 * @cfg {Number} height Sets the height of a cell
8941 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8942 * @cfg {Number} rowspan Sets the number of rows a cell should span
8943 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8944 * @cfg {String} valign Vertical aligns the content in a cell
8945 * @cfg {Number} width Specifies the width of a cell
8948 * Create a new TableCell
8949 * @param {Object} config The config object
8952 Roo.bootstrap.TableCell = function(config){
8953 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8956 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8976 getAutoCreate : function(){
8977 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8997 cfg.align=this.align
9003 cfg.bgcolor=this.bgcolor
9006 cfg.charoff=this.charoff
9009 cfg.colspan=this.colspan
9012 cfg.headers=this.headers
9015 cfg.height=this.height
9018 cfg.nowrap=this.nowrap
9021 cfg.rowspan=this.rowspan
9024 cfg.scope=this.scope
9027 cfg.valign=this.valign
9030 cfg.width=this.width
9049 * @class Roo.bootstrap.TableRow
9050 * @extends Roo.bootstrap.Component
9051 * Bootstrap TableRow class
9052 * @cfg {String} cls row class
9053 * @cfg {String} align Aligns the content in a table row
9054 * @cfg {String} bgcolor Specifies a background color for a table row
9055 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9056 * @cfg {String} valign Vertical aligns the content in a table row
9059 * Create a new TableRow
9060 * @param {Object} config The config object
9063 Roo.bootstrap.TableRow = function(config){
9064 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9067 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9075 getAutoCreate : function(){
9076 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9086 cfg.align = this.align;
9089 cfg.bgcolor = this.bgcolor;
9092 cfg.charoff = this.charoff;
9095 cfg.valign = this.valign;
9113 * @class Roo.bootstrap.TableBody
9114 * @extends Roo.bootstrap.Component
9115 * Bootstrap TableBody class
9116 * @cfg {String} cls element class
9117 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9118 * @cfg {String} align Aligns the content inside the element
9119 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9120 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9123 * Create a new TableBody
9124 * @param {Object} config The config object
9127 Roo.bootstrap.TableBody = function(config){
9128 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9131 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9139 getAutoCreate : function(){
9140 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9154 cfg.align = this.align;
9157 cfg.charoff = this.charoff;
9160 cfg.valign = this.valign;
9167 // initEvents : function()
9174 // this.store = Roo.factory(this.store, Roo.data);
9175 // this.store.on('load', this.onLoad, this);
9177 // this.store.load();
9181 // onLoad: function ()
9183 // this.fireEvent('load', this);
9193 * Ext JS Library 1.1.1
9194 * Copyright(c) 2006-2007, Ext JS, LLC.
9196 * Originally Released Under LGPL - original licence link has changed is not relivant.
9199 * <script type="text/javascript">
9202 // as we use this in bootstrap.
9203 Roo.namespace('Roo.form');
9205 * @class Roo.form.Action
9206 * Internal Class used to handle form actions
9208 * @param {Roo.form.BasicForm} el The form element or its id
9209 * @param {Object} config Configuration options
9214 // define the action interface
9215 Roo.form.Action = function(form, options){
9217 this.options = options || {};
9220 * Client Validation Failed
9223 Roo.form.Action.CLIENT_INVALID = 'client';
9225 * Server Validation Failed
9228 Roo.form.Action.SERVER_INVALID = 'server';
9230 * Connect to Server Failed
9233 Roo.form.Action.CONNECT_FAILURE = 'connect';
9235 * Reading Data from Server Failed
9238 Roo.form.Action.LOAD_FAILURE = 'load';
9240 Roo.form.Action.prototype = {
9242 failureType : undefined,
9243 response : undefined,
9247 run : function(options){
9252 success : function(response){
9257 handleResponse : function(response){
9261 // default connection failure
9262 failure : function(response){
9264 this.response = response;
9265 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9266 this.form.afterAction(this, false);
9269 processResponse : function(response){
9270 this.response = response;
9271 if(!response.responseText){
9274 this.result = this.handleResponse(response);
9278 // utility functions used internally
9279 getUrl : function(appendParams){
9280 var url = this.options.url || this.form.url || this.form.el.dom.action;
9282 var p = this.getParams();
9284 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9290 getMethod : function(){
9291 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9294 getParams : function(){
9295 var bp = this.form.baseParams;
9296 var p = this.options.params;
9298 if(typeof p == "object"){
9299 p = Roo.urlEncode(Roo.applyIf(p, bp));
9300 }else if(typeof p == 'string' && bp){
9301 p += '&' + Roo.urlEncode(bp);
9304 p = Roo.urlEncode(bp);
9309 createCallback : function(){
9311 success: this.success,
9312 failure: this.failure,
9314 timeout: (this.form.timeout*1000),
9315 upload: this.form.fileUpload ? this.success : undefined
9320 Roo.form.Action.Submit = function(form, options){
9321 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9324 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9327 haveProgress : false,
9328 uploadComplete : false,
9330 // uploadProgress indicator.
9331 uploadProgress : function()
9333 if (!this.form.progressUrl) {
9337 if (!this.haveProgress) {
9338 Roo.MessageBox.progress("Uploading", "Uploading");
9340 if (this.uploadComplete) {
9341 Roo.MessageBox.hide();
9345 this.haveProgress = true;
9347 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9349 var c = new Roo.data.Connection();
9351 url : this.form.progressUrl,
9356 success : function(req){
9357 //console.log(data);
9361 rdata = Roo.decode(req.responseText)
9363 Roo.log("Invalid data from server..");
9367 if (!rdata || !rdata.success) {
9369 Roo.MessageBox.alert(Roo.encode(rdata));
9372 var data = rdata.data;
9374 if (this.uploadComplete) {
9375 Roo.MessageBox.hide();
9380 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9381 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9384 this.uploadProgress.defer(2000,this);
9387 failure: function(data) {
9388 Roo.log('progress url failed ');
9399 // run get Values on the form, so it syncs any secondary forms.
9400 this.form.getValues();
9402 var o = this.options;
9403 var method = this.getMethod();
9404 var isPost = method == 'POST';
9405 if(o.clientValidation === false || this.form.isValid()){
9407 if (this.form.progressUrl) {
9408 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9409 (new Date() * 1) + '' + Math.random());
9414 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9415 form:this.form.el.dom,
9416 url:this.getUrl(!isPost),
9418 params:isPost ? this.getParams() : null,
9419 isUpload: this.form.fileUpload,
9420 formData : this.form.formData
9423 this.uploadProgress();
9425 }else if (o.clientValidation !== false){ // client validation failed
9426 this.failureType = Roo.form.Action.CLIENT_INVALID;
9427 this.form.afterAction(this, false);
9431 success : function(response)
9433 this.uploadComplete= true;
9434 if (this.haveProgress) {
9435 Roo.MessageBox.hide();
9439 var result = this.processResponse(response);
9440 if(result === true || result.success){
9441 this.form.afterAction(this, true);
9445 this.form.markInvalid(result.errors);
9446 this.failureType = Roo.form.Action.SERVER_INVALID;
9448 this.form.afterAction(this, false);
9450 failure : function(response)
9452 this.uploadComplete= true;
9453 if (this.haveProgress) {
9454 Roo.MessageBox.hide();
9457 this.response = response;
9458 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9459 this.form.afterAction(this, false);
9462 handleResponse : function(response){
9463 if(this.form.errorReader){
9464 var rs = this.form.errorReader.read(response);
9467 for(var i = 0, len = rs.records.length; i < len; i++) {
9468 var r = rs.records[i];
9472 if(errors.length < 1){
9476 success : rs.success,
9482 ret = Roo.decode(response.responseText);
9486 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9496 Roo.form.Action.Load = function(form, options){
9497 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9498 this.reader = this.form.reader;
9501 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9506 Roo.Ajax.request(Roo.apply(
9507 this.createCallback(), {
9508 method:this.getMethod(),
9509 url:this.getUrl(false),
9510 params:this.getParams()
9514 success : function(response){
9516 var result = this.processResponse(response);
9517 if(result === true || !result.success || !result.data){
9518 this.failureType = Roo.form.Action.LOAD_FAILURE;
9519 this.form.afterAction(this, false);
9522 this.form.clearInvalid();
9523 this.form.setValues(result.data);
9524 this.form.afterAction(this, true);
9527 handleResponse : function(response){
9528 if(this.form.reader){
9529 var rs = this.form.reader.read(response);
9530 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9532 success : rs.success,
9536 return Roo.decode(response.responseText);
9540 Roo.form.Action.ACTION_TYPES = {
9541 'load' : Roo.form.Action.Load,
9542 'submit' : Roo.form.Action.Submit
9551 * @class Roo.bootstrap.Form
9552 * @extends Roo.bootstrap.Component
9553 * Bootstrap Form class
9554 * @cfg {String} method GET | POST (default POST)
9555 * @cfg {String} labelAlign top | left (default top)
9556 * @cfg {String} align left | right - for navbars
9557 * @cfg {Boolean} loadMask load mask when submit (default true)
9562 * @param {Object} config The config object
9566 Roo.bootstrap.Form = function(config){
9568 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9570 Roo.bootstrap.Form.popover.apply();
9574 * @event clientvalidation
9575 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9576 * @param {Form} this
9577 * @param {Boolean} valid true if the form has passed client-side validation
9579 clientvalidation: true,
9581 * @event beforeaction
9582 * Fires before any action is performed. Return false to cancel the action.
9583 * @param {Form} this
9584 * @param {Action} action The action to be performed
9588 * @event actionfailed
9589 * Fires when an action fails.
9590 * @param {Form} this
9591 * @param {Action} action The action that failed
9593 actionfailed : true,
9595 * @event actioncomplete
9596 * Fires when an action is completed.
9597 * @param {Form} this
9598 * @param {Action} action The action that completed
9600 actioncomplete : true
9604 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9607 * @cfg {String} method
9608 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9613 * The URL to use for form actions if one isn't supplied in the action options.
9616 * @cfg {Boolean} fileUpload
9617 * Set to true if this form is a file upload.
9621 * @cfg {Object} baseParams
9622 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9626 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9630 * @cfg {Sting} align (left|right) for navbar forms
9635 activeAction : null,
9638 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9639 * element by passing it or its id or mask the form itself by passing in true.
9642 waitMsgTarget : false,
9647 * @cfg {Boolean} errorMask (true|false) default false
9652 * @cfg {Number} maskOffset Default 100
9657 * @cfg {Boolean} maskBody
9661 getAutoCreate : function(){
9665 method : this.method || 'POST',
9666 id : this.id || Roo.id(),
9669 if (this.parent().xtype.match(/^Nav/)) {
9670 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9674 if (this.labelAlign == 'left' ) {
9675 cfg.cls += ' form-horizontal';
9681 initEvents : function()
9683 this.el.on('submit', this.onSubmit, this);
9684 // this was added as random key presses on the form where triggering form submit.
9685 this.el.on('keypress', function(e) {
9686 if (e.getCharCode() != 13) {
9689 // we might need to allow it for textareas.. and some other items.
9690 // check e.getTarget().
9692 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9696 Roo.log("keypress blocked");
9704 onSubmit : function(e){
9709 * Returns true if client-side validation on the form is successful.
9712 isValid : function(){
9713 var items = this.getItems();
9717 items.each(function(f){
9723 Roo.log('invalid field: ' + f.name);
9727 if(!target && f.el.isVisible(true)){
9733 if(this.errorMask && !valid){
9734 Roo.bootstrap.Form.popover.mask(this, target);
9741 * Returns true if any fields in this form have changed since their original load.
9744 isDirty : function(){
9746 var items = this.getItems();
9747 items.each(function(f){
9757 * Performs a predefined action (submit or load) or custom actions you define on this form.
9758 * @param {String} actionName The name of the action type
9759 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9760 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9761 * accept other config options):
9763 Property Type Description
9764 ---------------- --------------- ----------------------------------------------------------------------------------
9765 url String The url for the action (defaults to the form's url)
9766 method String The form method to use (defaults to the form's method, or POST if not defined)
9767 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9768 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9769 validate the form on the client (defaults to false)
9771 * @return {BasicForm} this
9773 doAction : function(action, options){
9774 if(typeof action == 'string'){
9775 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9777 if(this.fireEvent('beforeaction', this, action) !== false){
9778 this.beforeAction(action);
9779 action.run.defer(100, action);
9785 beforeAction : function(action){
9786 var o = action.options;
9791 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9793 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9796 // not really supported yet.. ??
9798 //if(this.waitMsgTarget === true){
9799 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9800 //}else if(this.waitMsgTarget){
9801 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9802 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9804 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9810 afterAction : function(action, success){
9811 this.activeAction = null;
9812 var o = action.options;
9817 Roo.get(document.body).unmask();
9823 //if(this.waitMsgTarget === true){
9824 // this.el.unmask();
9825 //}else if(this.waitMsgTarget){
9826 // this.waitMsgTarget.unmask();
9828 // Roo.MessageBox.updateProgress(1);
9829 // Roo.MessageBox.hide();
9836 Roo.callback(o.success, o.scope, [this, action]);
9837 this.fireEvent('actioncomplete', this, action);
9841 // failure condition..
9842 // we have a scenario where updates need confirming.
9843 // eg. if a locking scenario exists..
9844 // we look for { errors : { needs_confirm : true }} in the response.
9846 (typeof(action.result) != 'undefined') &&
9847 (typeof(action.result.errors) != 'undefined') &&
9848 (typeof(action.result.errors.needs_confirm) != 'undefined')
9851 Roo.log("not supported yet");
9854 Roo.MessageBox.confirm(
9855 "Change requires confirmation",
9856 action.result.errorMsg,
9861 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9871 Roo.callback(o.failure, o.scope, [this, action]);
9872 // show an error message if no failed handler is set..
9873 if (!this.hasListener('actionfailed')) {
9874 Roo.log("need to add dialog support");
9876 Roo.MessageBox.alert("Error",
9877 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9878 action.result.errorMsg :
9879 "Saving Failed, please check your entries or try again"
9884 this.fireEvent('actionfailed', this, action);
9889 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9890 * @param {String} id The value to search for
9893 findField : function(id){
9894 var items = this.getItems();
9895 var field = items.get(id);
9897 items.each(function(f){
9898 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9905 return field || null;
9908 * Mark fields in this form invalid in bulk.
9909 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9910 * @return {BasicForm} this
9912 markInvalid : function(errors){
9913 if(errors instanceof Array){
9914 for(var i = 0, len = errors.length; i < len; i++){
9915 var fieldError = errors[i];
9916 var f = this.findField(fieldError.id);
9918 f.markInvalid(fieldError.msg);
9924 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9925 field.markInvalid(errors[id]);
9929 //Roo.each(this.childForms || [], function (f) {
9930 // f.markInvalid(errors);
9937 * Set values for fields in this form in bulk.
9938 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9939 * @return {BasicForm} this
9941 setValues : function(values){
9942 if(values instanceof Array){ // array of objects
9943 for(var i = 0, len = values.length; i < len; i++){
9945 var f = this.findField(v.id);
9947 f.setValue(v.value);
9948 if(this.trackResetOnLoad){
9949 f.originalValue = f.getValue();
9953 }else{ // object hash
9956 if(typeof values[id] != 'function' && (field = this.findField(id))){
9958 if (field.setFromData &&
9960 field.displayField &&
9961 // combos' with local stores can
9962 // be queried via setValue()
9963 // to set their value..
9964 (field.store && !field.store.isLocal)
9968 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9969 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9970 field.setFromData(sd);
9972 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9974 field.setFromData(values);
9977 field.setValue(values[id]);
9981 if(this.trackResetOnLoad){
9982 field.originalValue = field.getValue();
9988 //Roo.each(this.childForms || [], function (f) {
9989 // f.setValues(values);
9996 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9997 * they are returned as an array.
9998 * @param {Boolean} asString
10001 getValues : function(asString){
10002 //if (this.childForms) {
10003 // copy values from the child forms
10004 // Roo.each(this.childForms, function (f) {
10005 // this.setValues(f.getValues());
10011 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10012 if(asString === true){
10015 return Roo.urlDecode(fs);
10019 * Returns the fields in this form as an object with key/value pairs.
10020 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10023 getFieldValues : function(with_hidden)
10025 var items = this.getItems();
10027 items.each(function(f){
10029 if (!f.getName()) {
10033 var v = f.getValue();
10035 if (f.inputType =='radio') {
10036 if (typeof(ret[f.getName()]) == 'undefined') {
10037 ret[f.getName()] = ''; // empty..
10040 if (!f.el.dom.checked) {
10044 v = f.el.dom.value;
10048 if(f.xtype == 'MoneyField'){
10049 ret[f.currencyName] = f.getCurrency();
10052 // not sure if this supported any more..
10053 if ((typeof(v) == 'object') && f.getRawValue) {
10054 v = f.getRawValue() ; // dates..
10056 // combo boxes where name != hiddenName...
10057 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10058 ret[f.name] = f.getRawValue();
10060 ret[f.getName()] = v;
10067 * Clears all invalid messages in this form.
10068 * @return {BasicForm} this
10070 clearInvalid : function(){
10071 var items = this.getItems();
10073 items.each(function(f){
10081 * Resets this form.
10082 * @return {BasicForm} this
10084 reset : function(){
10085 var items = this.getItems();
10086 items.each(function(f){
10090 Roo.each(this.childForms || [], function (f) {
10098 getItems : function()
10100 var r=new Roo.util.MixedCollection(false, function(o){
10101 return o.id || (o.id = Roo.id());
10103 var iter = function(el) {
10110 Roo.each(el.items,function(e) {
10119 hideFields : function(items)
10121 Roo.each(items, function(i){
10123 var f = this.findField(i);
10134 showFields : function(items)
10136 Roo.each(items, function(i){
10138 var f = this.findField(i);
10151 Roo.apply(Roo.bootstrap.Form, {
10167 intervalID : false,
10173 if(this.isApplied){
10178 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10179 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10180 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10181 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10184 this.maskEl.top.enableDisplayMode("block");
10185 this.maskEl.left.enableDisplayMode("block");
10186 this.maskEl.bottom.enableDisplayMode("block");
10187 this.maskEl.right.enableDisplayMode("block");
10189 this.toolTip = new Roo.bootstrap.Tooltip({
10190 cls : 'roo-form-error-popover',
10192 'left' : ['r-l', [-2,0], 'right'],
10193 'right' : ['l-r', [2,0], 'left'],
10194 'bottom' : ['tl-bl', [0,2], 'top'],
10195 'top' : [ 'bl-tl', [0,-2], 'bottom']
10199 this.toolTip.render(Roo.get(document.body));
10201 this.toolTip.el.enableDisplayMode("block");
10203 Roo.get(document.body).on('click', function(){
10207 Roo.get(document.body).on('touchstart', function(){
10211 this.isApplied = true
10214 mask : function(form, target)
10218 this.target = target;
10220 if(!this.form.errorMask || !target.el){
10224 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10226 Roo.log(scrollable);
10228 var ot = this.target.el.calcOffsetsTo(scrollable);
10230 var scrollTo = ot[1] - this.form.maskOffset;
10232 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10234 scrollable.scrollTo('top', scrollTo);
10236 var box = this.target.el.getBox();
10238 var zIndex = Roo.bootstrap.Modal.zIndex++;
10241 this.maskEl.top.setStyle('position', 'absolute');
10242 this.maskEl.top.setStyle('z-index', zIndex);
10243 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10244 this.maskEl.top.setLeft(0);
10245 this.maskEl.top.setTop(0);
10246 this.maskEl.top.show();
10248 this.maskEl.left.setStyle('position', 'absolute');
10249 this.maskEl.left.setStyle('z-index', zIndex);
10250 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10251 this.maskEl.left.setLeft(0);
10252 this.maskEl.left.setTop(box.y - this.padding);
10253 this.maskEl.left.show();
10255 this.maskEl.bottom.setStyle('position', 'absolute');
10256 this.maskEl.bottom.setStyle('z-index', zIndex);
10257 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10258 this.maskEl.bottom.setLeft(0);
10259 this.maskEl.bottom.setTop(box.bottom + this.padding);
10260 this.maskEl.bottom.show();
10262 this.maskEl.right.setStyle('position', 'absolute');
10263 this.maskEl.right.setStyle('z-index', zIndex);
10264 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10265 this.maskEl.right.setLeft(box.right + this.padding);
10266 this.maskEl.right.setTop(box.y - this.padding);
10267 this.maskEl.right.show();
10269 this.toolTip.bindEl = this.target.el;
10271 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10273 var tip = this.target.blankText;
10275 if(this.target.getValue() !== '' ) {
10277 if (this.target.invalidText.length) {
10278 tip = this.target.invalidText;
10279 } else if (this.target.regexText.length){
10280 tip = this.target.regexText;
10284 this.toolTip.show(tip);
10286 this.intervalID = window.setInterval(function() {
10287 Roo.bootstrap.Form.popover.unmask();
10290 window.onwheel = function(){ return false;};
10292 (function(){ this.isMasked = true; }).defer(500, this);
10296 unmask : function()
10298 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10302 this.maskEl.top.setStyle('position', 'absolute');
10303 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10304 this.maskEl.top.hide();
10306 this.maskEl.left.setStyle('position', 'absolute');
10307 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10308 this.maskEl.left.hide();
10310 this.maskEl.bottom.setStyle('position', 'absolute');
10311 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10312 this.maskEl.bottom.hide();
10314 this.maskEl.right.setStyle('position', 'absolute');
10315 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10316 this.maskEl.right.hide();
10318 this.toolTip.hide();
10320 this.toolTip.el.hide();
10322 window.onwheel = function(){ return true;};
10324 if(this.intervalID){
10325 window.clearInterval(this.intervalID);
10326 this.intervalID = false;
10329 this.isMasked = false;
10339 * Ext JS Library 1.1.1
10340 * Copyright(c) 2006-2007, Ext JS, LLC.
10342 * Originally Released Under LGPL - original licence link has changed is not relivant.
10345 * <script type="text/javascript">
10348 * @class Roo.form.VTypes
10349 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10352 Roo.form.VTypes = function(){
10353 // closure these in so they are only created once.
10354 var alpha = /^[a-zA-Z_]+$/;
10355 var alphanum = /^[a-zA-Z0-9_]+$/;
10356 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10357 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10359 // All these messages and functions are configurable
10362 * The function used to validate email addresses
10363 * @param {String} value The email address
10365 'email' : function(v){
10366 return email.test(v);
10369 * The error text to display when the email validation function returns false
10372 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10374 * The keystroke filter mask to be applied on email input
10377 'emailMask' : /[a-z0-9_\.\-@]/i,
10380 * The function used to validate URLs
10381 * @param {String} value The URL
10383 'url' : function(v){
10384 return url.test(v);
10387 * The error text to display when the url validation function returns false
10390 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10393 * The function used to validate alpha values
10394 * @param {String} value The value
10396 'alpha' : function(v){
10397 return alpha.test(v);
10400 * The error text to display when the alpha validation function returns false
10403 'alphaText' : 'This field should only contain letters and _',
10405 * The keystroke filter mask to be applied on alpha input
10408 'alphaMask' : /[a-z_]/i,
10411 * The function used to validate alphanumeric values
10412 * @param {String} value The value
10414 'alphanum' : function(v){
10415 return alphanum.test(v);
10418 * The error text to display when the alphanumeric validation function returns false
10421 'alphanumText' : 'This field should only contain letters, numbers and _',
10423 * The keystroke filter mask to be applied on alphanumeric input
10426 'alphanumMask' : /[a-z0-9_]/i
10436 * @class Roo.bootstrap.Input
10437 * @extends Roo.bootstrap.Component
10438 * Bootstrap Input class
10439 * @cfg {Boolean} disabled is it disabled
10440 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10441 * @cfg {String} name name of the input
10442 * @cfg {string} fieldLabel - the label associated
10443 * @cfg {string} placeholder - placeholder to put in text.
10444 * @cfg {string} before - input group add on before
10445 * @cfg {string} after - input group add on after
10446 * @cfg {string} size - (lg|sm) or leave empty..
10447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10449 * @cfg {Number} md colspan out of 12 for computer-sized screens
10450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10451 * @cfg {string} value default value of the input
10452 * @cfg {Number} labelWidth set the width of label
10453 * @cfg {Number} labellg set the width of label (1-12)
10454 * @cfg {Number} labelmd set the width of label (1-12)
10455 * @cfg {Number} labelsm set the width of label (1-12)
10456 * @cfg {Number} labelxs set the width of label (1-12)
10457 * @cfg {String} labelAlign (top|left)
10458 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10459 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10460 * @cfg {String} indicatorpos (left|right) default left
10461 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10462 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10463 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10465 * @cfg {String} align (left|center|right) Default left
10466 * @cfg {Boolean} forceFeedback (true|false) Default false
10469 * Create a new Input
10470 * @param {Object} config The config object
10473 Roo.bootstrap.Input = function(config){
10475 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10480 * Fires when this field receives input focus.
10481 * @param {Roo.form.Field} this
10486 * Fires when this field loses input focus.
10487 * @param {Roo.form.Field} this
10491 * @event specialkey
10492 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10493 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10494 * @param {Roo.form.Field} this
10495 * @param {Roo.EventObject} e The event object
10500 * Fires just before the field blurs if the field value has changed.
10501 * @param {Roo.form.Field} this
10502 * @param {Mixed} newValue The new value
10503 * @param {Mixed} oldValue The original value
10508 * Fires after the field has been marked as invalid.
10509 * @param {Roo.form.Field} this
10510 * @param {String} msg The validation message
10515 * Fires after the field has been validated with no errors.
10516 * @param {Roo.form.Field} this
10521 * Fires after the key up
10522 * @param {Roo.form.Field} this
10523 * @param {Roo.EventObject} e The event Object
10528 * Fires after the user pastes into input
10529 * @param {Roo.form.Field} this
10530 * @param {Roo.EventObject} e The event Object
10536 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10538 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10539 automatic validation (defaults to "keyup").
10541 validationEvent : "keyup",
10543 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10545 validateOnBlur : true,
10547 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10549 validationDelay : 250,
10551 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10553 focusClass : "x-form-focus", // not needed???
10557 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10559 invalidClass : "has-warning",
10562 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10564 validClass : "has-success",
10567 * @cfg {Boolean} hasFeedback (true|false) default true
10569 hasFeedback : true,
10572 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10574 invalidFeedbackClass : "glyphicon-warning-sign",
10577 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10579 validFeedbackClass : "glyphicon-ok",
10582 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10584 selectOnFocus : false,
10587 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10591 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10596 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10598 disableKeyFilter : false,
10601 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10605 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10609 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10611 blankText : "Please complete this mandatory field",
10614 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10618 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10620 maxLength : Number.MAX_VALUE,
10622 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10624 minLengthText : "The minimum length for this field is {0}",
10626 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10628 maxLengthText : "The maximum length for this field is {0}",
10632 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10633 * If available, this function will be called only after the basic validators all return true, and will be passed the
10634 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10638 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10639 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10640 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10644 * @cfg {String} regexText -- Depricated - use Invalid Text
10649 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10655 autocomplete: false,
10659 inputType : 'text',
10662 placeholder: false,
10667 preventMark: false,
10668 isFormField : true,
10671 labelAlign : false,
10674 formatedValue : false,
10675 forceFeedback : false,
10677 indicatorpos : 'left',
10687 parentLabelAlign : function()
10690 while (parent.parent()) {
10691 parent = parent.parent();
10692 if (typeof(parent.labelAlign) !='undefined') {
10693 return parent.labelAlign;
10700 getAutoCreate : function()
10702 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10708 if(this.inputType != 'hidden'){
10709 cfg.cls = 'form-group' //input-group
10715 type : this.inputType,
10716 value : this.value,
10717 cls : 'form-control',
10718 placeholder : this.placeholder || '',
10719 autocomplete : this.autocomplete || 'new-password'
10721 if (this.inputType == 'file') {
10722 input.style = 'overflow:hidden'; // why not in CSS?
10725 if(this.capture.length){
10726 input.capture = this.capture;
10729 if(this.accept.length){
10730 input.accept = this.accept + "/*";
10734 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10737 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10738 input.maxLength = this.maxLength;
10741 if (this.disabled) {
10742 input.disabled=true;
10745 if (this.readOnly) {
10746 input.readonly=true;
10750 input.name = this.name;
10754 input.cls += ' input-' + this.size;
10758 ['xs','sm','md','lg'].map(function(size){
10759 if (settings[size]) {
10760 cfg.cls += ' col-' + size + '-' + settings[size];
10764 var inputblock = input;
10768 cls: 'glyphicon form-control-feedback'
10771 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10774 cls : 'has-feedback',
10782 if (this.before || this.after) {
10785 cls : 'input-group',
10789 if (this.before && typeof(this.before) == 'string') {
10791 inputblock.cn.push({
10793 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10797 if (this.before && typeof(this.before) == 'object') {
10798 this.before = Roo.factory(this.before);
10800 inputblock.cn.push({
10802 cls : 'roo-input-before input-group-prepend input-group-' +
10803 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10807 inputblock.cn.push(input);
10809 if (this.after && typeof(this.after) == 'string') {
10810 inputblock.cn.push({
10812 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10816 if (this.after && typeof(this.after) == 'object') {
10817 this.after = Roo.factory(this.after);
10819 inputblock.cn.push({
10821 cls : 'roo-input-after input-group-append input-group-' +
10822 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10826 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10827 inputblock.cls += ' has-feedback';
10828 inputblock.cn.push(feedback);
10833 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10834 tooltip : 'This field is required'
10836 if (this.allowBlank ) {
10837 indicator.style = this.allowBlank ? ' display:none' : '';
10839 if (align ==='left' && this.fieldLabel.length) {
10841 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10848 cls : 'control-label col-form-label',
10849 html : this.fieldLabel
10860 var labelCfg = cfg.cn[1];
10861 var contentCfg = cfg.cn[2];
10863 if(this.indicatorpos == 'right'){
10868 cls : 'control-label col-form-label',
10872 html : this.fieldLabel
10886 labelCfg = cfg.cn[0];
10887 contentCfg = cfg.cn[1];
10891 if(this.labelWidth > 12){
10892 labelCfg.style = "width: " + this.labelWidth + 'px';
10895 if(this.labelWidth < 13 && this.labelmd == 0){
10896 this.labelmd = this.labelWidth;
10899 if(this.labellg > 0){
10900 labelCfg.cls += ' col-lg-' + this.labellg;
10901 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10904 if(this.labelmd > 0){
10905 labelCfg.cls += ' col-md-' + this.labelmd;
10906 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10909 if(this.labelsm > 0){
10910 labelCfg.cls += ' col-sm-' + this.labelsm;
10911 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10914 if(this.labelxs > 0){
10915 labelCfg.cls += ' col-xs-' + this.labelxs;
10916 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10920 } else if ( this.fieldLabel.length) {
10927 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10928 tooltip : 'This field is required',
10929 style : this.allowBlank ? ' display:none' : ''
10933 //cls : 'input-group-addon',
10934 html : this.fieldLabel
10942 if(this.indicatorpos == 'right'){
10947 //cls : 'input-group-addon',
10948 html : this.fieldLabel
10953 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10954 tooltip : 'This field is required',
10955 style : this.allowBlank ? ' display:none' : ''
10975 if (this.parentType === 'Navbar' && this.parent().bar) {
10976 cfg.cls += ' navbar-form';
10979 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10980 // on BS4 we do this only if not form
10981 cfg.cls += ' navbar-form';
10989 * return the real input element.
10991 inputEl: function ()
10993 return this.el.select('input.form-control',true).first();
10996 tooltipEl : function()
10998 return this.inputEl();
11001 indicatorEl : function()
11003 if (Roo.bootstrap.version == 4) {
11004 return false; // not enabled in v4 yet.
11007 var indicator = this.el.select('i.roo-required-indicator',true).first();
11017 setDisabled : function(v)
11019 var i = this.inputEl().dom;
11021 i.removeAttribute('disabled');
11025 i.setAttribute('disabled','true');
11027 initEvents : function()
11030 this.inputEl().on("keydown" , this.fireKey, this);
11031 this.inputEl().on("focus", this.onFocus, this);
11032 this.inputEl().on("blur", this.onBlur, this);
11034 this.inputEl().relayEvent('keyup', this);
11035 this.inputEl().relayEvent('paste', this);
11037 this.indicator = this.indicatorEl();
11039 if(this.indicator){
11040 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11043 // reference to original value for reset
11044 this.originalValue = this.getValue();
11045 //Roo.form.TextField.superclass.initEvents.call(this);
11046 if(this.validationEvent == 'keyup'){
11047 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11048 this.inputEl().on('keyup', this.filterValidation, this);
11050 else if(this.validationEvent !== false){
11051 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11054 if(this.selectOnFocus){
11055 this.on("focus", this.preFocus, this);
11058 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11059 this.inputEl().on("keypress", this.filterKeys, this);
11061 this.inputEl().relayEvent('keypress', this);
11064 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11065 this.el.on("click", this.autoSize, this);
11068 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11069 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11072 if (typeof(this.before) == 'object') {
11073 this.before.render(this.el.select('.roo-input-before',true).first());
11075 if (typeof(this.after) == 'object') {
11076 this.after.render(this.el.select('.roo-input-after',true).first());
11079 this.inputEl().on('change', this.onChange, this);
11082 filterValidation : function(e){
11083 if(!e.isNavKeyPress()){
11084 this.validationTask.delay(this.validationDelay);
11088 * Validates the field value
11089 * @return {Boolean} True if the value is valid, else false
11091 validate : function(){
11092 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11093 if(this.disabled || this.validateValue(this.getRawValue())){
11098 this.markInvalid();
11104 * Validates a value according to the field's validation rules and marks the field as invalid
11105 * if the validation fails
11106 * @param {Mixed} value The value to validate
11107 * @return {Boolean} True if the value is valid, else false
11109 validateValue : function(value)
11111 if(this.getVisibilityEl().hasClass('hidden')){
11115 if(value.length < 1) { // if it's blank
11116 if(this.allowBlank){
11122 if(value.length < this.minLength){
11125 if(value.length > this.maxLength){
11129 var vt = Roo.form.VTypes;
11130 if(!vt[this.vtype](value, this)){
11134 if(typeof this.validator == "function"){
11135 var msg = this.validator(value);
11139 if (typeof(msg) == 'string') {
11140 this.invalidText = msg;
11144 if(this.regex && !this.regex.test(value)){
11152 fireKey : function(e){
11153 //Roo.log('field ' + e.getKey());
11154 if(e.isNavKeyPress()){
11155 this.fireEvent("specialkey", this, e);
11158 focus : function (selectText){
11160 this.inputEl().focus();
11161 if(selectText === true){
11162 this.inputEl().dom.select();
11168 onFocus : function(){
11169 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11170 // this.el.addClass(this.focusClass);
11172 if(!this.hasFocus){
11173 this.hasFocus = true;
11174 this.startValue = this.getValue();
11175 this.fireEvent("focus", this);
11179 beforeBlur : Roo.emptyFn,
11183 onBlur : function(){
11185 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11186 //this.el.removeClass(this.focusClass);
11188 this.hasFocus = false;
11189 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11192 var v = this.getValue();
11193 if(String(v) !== String(this.startValue)){
11194 this.fireEvent('change', this, v, this.startValue);
11196 this.fireEvent("blur", this);
11199 onChange : function(e)
11201 var v = this.getValue();
11202 if(String(v) !== String(this.startValue)){
11203 this.fireEvent('change', this, v, this.startValue);
11209 * Resets the current field value to the originally loaded value and clears any validation messages
11211 reset : function(){
11212 this.setValue(this.originalValue);
11216 * Returns the name of the field
11217 * @return {Mixed} name The name field
11219 getName: function(){
11223 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11224 * @return {Mixed} value The field value
11226 getValue : function(){
11228 var v = this.inputEl().getValue();
11233 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11234 * @return {Mixed} value The field value
11236 getRawValue : function(){
11237 var v = this.inputEl().getValue();
11243 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11244 * @param {Mixed} value The value to set
11246 setRawValue : function(v){
11247 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11250 selectText : function(start, end){
11251 var v = this.getRawValue();
11253 start = start === undefined ? 0 : start;
11254 end = end === undefined ? v.length : end;
11255 var d = this.inputEl().dom;
11256 if(d.setSelectionRange){
11257 d.setSelectionRange(start, end);
11258 }else if(d.createTextRange){
11259 var range = d.createTextRange();
11260 range.moveStart("character", start);
11261 range.moveEnd("character", v.length-end);
11268 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11269 * @param {Mixed} value The value to set
11271 setValue : function(v){
11274 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11280 processValue : function(value){
11281 if(this.stripCharsRe){
11282 var newValue = value.replace(this.stripCharsRe, '');
11283 if(newValue !== value){
11284 this.setRawValue(newValue);
11291 preFocus : function(){
11293 if(this.selectOnFocus){
11294 this.inputEl().dom.select();
11297 filterKeys : function(e){
11298 var k = e.getKey();
11299 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11302 var c = e.getCharCode(), cc = String.fromCharCode(c);
11303 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11306 if(!this.maskRe.test(cc)){
11311 * Clear any invalid styles/messages for this field
11313 clearInvalid : function(){
11315 if(!this.el || this.preventMark){ // not rendered
11320 this.el.removeClass([this.invalidClass, 'is-invalid']);
11322 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11324 var feedback = this.el.select('.form-control-feedback', true).first();
11327 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11332 if(this.indicator){
11333 this.indicator.removeClass('visible');
11334 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11337 this.fireEvent('valid', this);
11341 * Mark this field as valid
11343 markValid : function()
11345 if(!this.el || this.preventMark){ // not rendered...
11349 this.el.removeClass([this.invalidClass, this.validClass]);
11350 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11352 var feedback = this.el.select('.form-control-feedback', true).first();
11355 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11358 if(this.indicator){
11359 this.indicator.removeClass('visible');
11360 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11368 if(this.allowBlank && !this.getRawValue().length){
11371 if (Roo.bootstrap.version == 3) {
11372 this.el.addClass(this.validClass);
11374 this.inputEl().addClass('is-valid');
11377 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11379 var feedback = this.el.select('.form-control-feedback', true).first();
11382 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11383 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11388 this.fireEvent('valid', this);
11392 * Mark this field as invalid
11393 * @param {String} msg The validation message
11395 markInvalid : function(msg)
11397 if(!this.el || this.preventMark){ // not rendered
11401 this.el.removeClass([this.invalidClass, this.validClass]);
11402 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11404 var feedback = this.el.select('.form-control-feedback', true).first();
11407 this.el.select('.form-control-feedback', true).first().removeClass(
11408 [this.invalidFeedbackClass, this.validFeedbackClass]);
11415 if(this.allowBlank && !this.getRawValue().length){
11419 if(this.indicator){
11420 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11421 this.indicator.addClass('visible');
11423 if (Roo.bootstrap.version == 3) {
11424 this.el.addClass(this.invalidClass);
11426 this.inputEl().addClass('is-invalid');
11431 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11433 var feedback = this.el.select('.form-control-feedback', true).first();
11436 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11438 if(this.getValue().length || this.forceFeedback){
11439 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11446 this.fireEvent('invalid', this, msg);
11449 SafariOnKeyDown : function(event)
11451 // this is a workaround for a password hang bug on chrome/ webkit.
11452 if (this.inputEl().dom.type != 'password') {
11456 var isSelectAll = false;
11458 if(this.inputEl().dom.selectionEnd > 0){
11459 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11461 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11462 event.preventDefault();
11467 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11469 event.preventDefault();
11470 // this is very hacky as keydown always get's upper case.
11472 var cc = String.fromCharCode(event.getCharCode());
11473 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11477 adjustWidth : function(tag, w){
11478 tag = tag.toLowerCase();
11479 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11480 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11481 if(tag == 'input'){
11484 if(tag == 'textarea'){
11487 }else if(Roo.isOpera){
11488 if(tag == 'input'){
11491 if(tag == 'textarea'){
11499 setFieldLabel : function(v)
11501 if(!this.rendered){
11505 if(this.indicatorEl()){
11506 var ar = this.el.select('label > span',true);
11508 if (ar.elements.length) {
11509 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11510 this.fieldLabel = v;
11514 var br = this.el.select('label',true);
11516 if(br.elements.length) {
11517 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11518 this.fieldLabel = v;
11522 Roo.log('Cannot Found any of label > span || label in input');
11526 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11527 this.fieldLabel = v;
11542 * @class Roo.bootstrap.TextArea
11543 * @extends Roo.bootstrap.Input
11544 * Bootstrap TextArea class
11545 * @cfg {Number} cols Specifies the visible width of a text area
11546 * @cfg {Number} rows Specifies the visible number of lines in a text area
11547 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11548 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11549 * @cfg {string} html text
11552 * Create a new TextArea
11553 * @param {Object} config The config object
11556 Roo.bootstrap.TextArea = function(config){
11557 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11561 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11571 getAutoCreate : function(){
11573 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11579 if(this.inputType != 'hidden'){
11580 cfg.cls = 'form-group' //input-group
11588 value : this.value || '',
11589 html: this.html || '',
11590 cls : 'form-control',
11591 placeholder : this.placeholder || ''
11595 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11596 input.maxLength = this.maxLength;
11600 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11604 input.cols = this.cols;
11607 if (this.readOnly) {
11608 input.readonly = true;
11612 input.name = this.name;
11616 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11620 ['xs','sm','md','lg'].map(function(size){
11621 if (settings[size]) {
11622 cfg.cls += ' col-' + size + '-' + settings[size];
11626 var inputblock = input;
11628 if(this.hasFeedback && !this.allowBlank){
11632 cls: 'glyphicon form-control-feedback'
11636 cls : 'has-feedback',
11645 if (this.before || this.after) {
11648 cls : 'input-group',
11652 inputblock.cn.push({
11654 cls : 'input-group-addon',
11659 inputblock.cn.push(input);
11661 if(this.hasFeedback && !this.allowBlank){
11662 inputblock.cls += ' has-feedback';
11663 inputblock.cn.push(feedback);
11667 inputblock.cn.push({
11669 cls : 'input-group-addon',
11676 if (align ==='left' && this.fieldLabel.length) {
11681 cls : 'control-label',
11682 html : this.fieldLabel
11693 if(this.labelWidth > 12){
11694 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11697 if(this.labelWidth < 13 && this.labelmd == 0){
11698 this.labelmd = this.labelWidth;
11701 if(this.labellg > 0){
11702 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11703 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11706 if(this.labelmd > 0){
11707 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11708 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11711 if(this.labelsm > 0){
11712 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11713 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11716 if(this.labelxs > 0){
11717 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11718 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11721 } else if ( this.fieldLabel.length) {
11726 //cls : 'input-group-addon',
11727 html : this.fieldLabel
11745 if (this.disabled) {
11746 input.disabled=true;
11753 * return the real textarea element.
11755 inputEl: function ()
11757 return this.el.select('textarea.form-control',true).first();
11761 * Clear any invalid styles/messages for this field
11763 clearInvalid : function()
11766 if(!this.el || this.preventMark){ // not rendered
11770 var label = this.el.select('label', true).first();
11771 var icon = this.el.select('i.fa-star', true).first();
11776 this.el.removeClass( this.validClass);
11777 this.inputEl().removeClass('is-invalid');
11779 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11781 var feedback = this.el.select('.form-control-feedback', true).first();
11784 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11789 this.fireEvent('valid', this);
11793 * Mark this field as valid
11795 markValid : function()
11797 if(!this.el || this.preventMark){ // not rendered
11801 this.el.removeClass([this.invalidClass, this.validClass]);
11802 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11804 var feedback = this.el.select('.form-control-feedback', true).first();
11807 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11810 if(this.disabled || this.allowBlank){
11814 var label = this.el.select('label', true).first();
11815 var icon = this.el.select('i.fa-star', true).first();
11820 if (Roo.bootstrap.version == 3) {
11821 this.el.addClass(this.validClass);
11823 this.inputEl().addClass('is-valid');
11827 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11829 var feedback = this.el.select('.form-control-feedback', true).first();
11832 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11833 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11838 this.fireEvent('valid', this);
11842 * Mark this field as invalid
11843 * @param {String} msg The validation message
11845 markInvalid : function(msg)
11847 if(!this.el || this.preventMark){ // not rendered
11851 this.el.removeClass([this.invalidClass, this.validClass]);
11852 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11854 var feedback = this.el.select('.form-control-feedback', true).first();
11857 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11860 if(this.disabled || this.allowBlank){
11864 var label = this.el.select('label', true).first();
11865 var icon = this.el.select('i.fa-star', true).first();
11867 if(!this.getValue().length && label && !icon){
11868 this.el.createChild({
11870 cls : 'text-danger fa fa-lg fa-star',
11871 tooltip : 'This field is required',
11872 style : 'margin-right:5px;'
11876 if (Roo.bootstrap.version == 3) {
11877 this.el.addClass(this.invalidClass);
11879 this.inputEl().addClass('is-invalid');
11882 // fixme ... this may be depricated need to test..
11883 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11885 var feedback = this.el.select('.form-control-feedback', true).first();
11888 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11890 if(this.getValue().length || this.forceFeedback){
11891 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11898 this.fireEvent('invalid', this, msg);
11906 * trigger field - base class for combo..
11911 * @class Roo.bootstrap.TriggerField
11912 * @extends Roo.bootstrap.Input
11913 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11914 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11915 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11916 * for which you can provide a custom implementation. For example:
11918 var trigger = new Roo.bootstrap.TriggerField();
11919 trigger.onTriggerClick = myTriggerFn;
11920 trigger.applyTo('my-field');
11923 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11924 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11925 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11926 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11927 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11930 * Create a new TriggerField.
11931 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11932 * to the base TextField)
11934 Roo.bootstrap.TriggerField = function(config){
11935 this.mimicing = false;
11936 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11939 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11941 * @cfg {String} triggerClass A CSS class to apply to the trigger
11944 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11949 * @cfg {Boolean} removable (true|false) special filter default false
11953 /** @cfg {Boolean} grow @hide */
11954 /** @cfg {Number} growMin @hide */
11955 /** @cfg {Number} growMax @hide */
11961 autoSize: Roo.emptyFn,
11965 deferHeight : true,
11968 actionMode : 'wrap',
11973 getAutoCreate : function(){
11975 var align = this.labelAlign || this.parentLabelAlign();
11980 cls: 'form-group' //input-group
11987 type : this.inputType,
11988 cls : 'form-control',
11989 autocomplete: 'new-password',
11990 placeholder : this.placeholder || ''
11994 input.name = this.name;
11997 input.cls += ' input-' + this.size;
12000 if (this.disabled) {
12001 input.disabled=true;
12004 var inputblock = input;
12006 if(this.hasFeedback && !this.allowBlank){
12010 cls: 'glyphicon form-control-feedback'
12013 if(this.removable && !this.editable ){
12015 cls : 'has-feedback',
12021 cls : 'roo-combo-removable-btn close'
12028 cls : 'has-feedback',
12037 if(this.removable && !this.editable ){
12039 cls : 'roo-removable',
12045 cls : 'roo-combo-removable-btn close'
12052 if (this.before || this.after) {
12055 cls : 'input-group',
12059 inputblock.cn.push({
12061 cls : 'input-group-addon input-group-prepend input-group-text',
12066 inputblock.cn.push(input);
12068 if(this.hasFeedback && !this.allowBlank){
12069 inputblock.cls += ' has-feedback';
12070 inputblock.cn.push(feedback);
12074 inputblock.cn.push({
12076 cls : 'input-group-addon input-group-append input-group-text',
12085 var ibwrap = inputblock;
12090 cls: 'roo-select2-choices',
12094 cls: 'roo-select2-search-field',
12106 cls: 'roo-select2-container input-group',
12111 cls: 'form-hidden-field'
12117 if(!this.multiple && this.showToggleBtn){
12123 if (this.caret != false) {
12126 cls: 'fa fa-' + this.caret
12133 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12135 Roo.bootstrap.version == 3 ? caret : '',
12138 cls: 'combobox-clear',
12152 combobox.cls += ' roo-select2-container-multi';
12156 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12157 tooltip : 'This field is required'
12159 if (Roo.bootstrap.version == 4) {
12162 style : 'display:none'
12167 if (align ==='left' && this.fieldLabel.length) {
12169 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12176 cls : 'control-label',
12177 html : this.fieldLabel
12189 var labelCfg = cfg.cn[1];
12190 var contentCfg = cfg.cn[2];
12192 if(this.indicatorpos == 'right'){
12197 cls : 'control-label',
12201 html : this.fieldLabel
12215 labelCfg = cfg.cn[0];
12216 contentCfg = cfg.cn[1];
12219 if(this.labelWidth > 12){
12220 labelCfg.style = "width: " + this.labelWidth + 'px';
12223 if(this.labelWidth < 13 && this.labelmd == 0){
12224 this.labelmd = this.labelWidth;
12227 if(this.labellg > 0){
12228 labelCfg.cls += ' col-lg-' + this.labellg;
12229 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12232 if(this.labelmd > 0){
12233 labelCfg.cls += ' col-md-' + this.labelmd;
12234 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12237 if(this.labelsm > 0){
12238 labelCfg.cls += ' col-sm-' + this.labelsm;
12239 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12242 if(this.labelxs > 0){
12243 labelCfg.cls += ' col-xs-' + this.labelxs;
12244 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12247 } else if ( this.fieldLabel.length) {
12248 // Roo.log(" label");
12253 //cls : 'input-group-addon',
12254 html : this.fieldLabel
12262 if(this.indicatorpos == 'right'){
12270 html : this.fieldLabel
12284 // Roo.log(" no label && no align");
12291 ['xs','sm','md','lg'].map(function(size){
12292 if (settings[size]) {
12293 cfg.cls += ' col-' + size + '-' + settings[size];
12304 onResize : function(w, h){
12305 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12306 // if(typeof w == 'number'){
12307 // var x = w - this.trigger.getWidth();
12308 // this.inputEl().setWidth(this.adjustWidth('input', x));
12309 // this.trigger.setStyle('left', x+'px');
12314 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12317 getResizeEl : function(){
12318 return this.inputEl();
12322 getPositionEl : function(){
12323 return this.inputEl();
12327 alignErrorIcon : function(){
12328 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12332 initEvents : function(){
12336 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12337 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12338 if(!this.multiple && this.showToggleBtn){
12339 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12340 if(this.hideTrigger){
12341 this.trigger.setDisplayed(false);
12343 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12347 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12350 if(this.removable && !this.editable && !this.tickable){
12351 var close = this.closeTriggerEl();
12354 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12355 close.on('click', this.removeBtnClick, this, close);
12359 //this.trigger.addClassOnOver('x-form-trigger-over');
12360 //this.trigger.addClassOnClick('x-form-trigger-click');
12363 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12367 closeTriggerEl : function()
12369 var close = this.el.select('.roo-combo-removable-btn', true).first();
12370 return close ? close : false;
12373 removeBtnClick : function(e, h, el)
12375 e.preventDefault();
12377 if(this.fireEvent("remove", this) !== false){
12379 this.fireEvent("afterremove", this)
12383 createList : function()
12385 this.list = Roo.get(document.body).createChild({
12386 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12387 cls: 'typeahead typeahead-long dropdown-menu shadow',
12388 style: 'display:none'
12391 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12396 initTrigger : function(){
12401 onDestroy : function(){
12403 this.trigger.removeAllListeners();
12404 // this.trigger.remove();
12407 // this.wrap.remove();
12409 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12413 onFocus : function(){
12414 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12416 if(!this.mimicing){
12417 this.wrap.addClass('x-trigger-wrap-focus');
12418 this.mimicing = true;
12419 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12420 if(this.monitorTab){
12421 this.el.on("keydown", this.checkTab, this);
12428 checkTab : function(e){
12429 if(e.getKey() == e.TAB){
12430 this.triggerBlur();
12435 onBlur : function(){
12440 mimicBlur : function(e, t){
12442 if(!this.wrap.contains(t) && this.validateBlur()){
12443 this.triggerBlur();
12449 triggerBlur : function(){
12450 this.mimicing = false;
12451 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12452 if(this.monitorTab){
12453 this.el.un("keydown", this.checkTab, this);
12455 //this.wrap.removeClass('x-trigger-wrap-focus');
12456 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12460 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12461 validateBlur : function(e, t){
12466 onDisable : function(){
12467 this.inputEl().dom.disabled = true;
12468 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12470 // this.wrap.addClass('x-item-disabled');
12475 onEnable : function(){
12476 this.inputEl().dom.disabled = false;
12477 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12479 // this.el.removeClass('x-item-disabled');
12484 onShow : function(){
12485 var ae = this.getActionEl();
12488 ae.dom.style.display = '';
12489 ae.dom.style.visibility = 'visible';
12495 onHide : function(){
12496 var ae = this.getActionEl();
12497 ae.dom.style.display = 'none';
12501 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12502 * by an implementing function.
12504 * @param {EventObject} e
12506 onTriggerClick : Roo.emptyFn
12514 * @class Roo.bootstrap.CardUploader
12515 * @extends Roo.bootstrap.Button
12516 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12517 * @cfg {Number} errorTimeout default 3000
12518 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12519 * @cfg {Array} html The button text.
12523 * Create a new CardUploader
12524 * @param {Object} config The config object
12527 Roo.bootstrap.CardUploader = function(config){
12531 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12534 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12542 * When a image is clicked on - and needs to display a slideshow or similar..
12543 * @param {Roo.bootstrap.Card} this
12544 * @param {Object} The image information data
12550 * When a the download link is clicked
12551 * @param {Roo.bootstrap.Card} this
12552 * @param {Object} The image information data contains
12559 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12562 errorTimeout : 3000,
12566 fileCollection : false,
12569 getAutoCreate : function()
12573 cls :'form-group' ,
12578 //cls : 'input-group-addon',
12579 html : this.fieldLabel
12587 value : this.value,
12588 cls : 'd-none form-control'
12593 multiple : 'multiple',
12595 cls : 'd-none roo-card-upload-selector'
12599 cls : 'roo-card-uploader-button-container w-100 mb-2'
12602 cls : 'card-columns roo-card-uploader-container'
12612 getChildContainer : function() /// what children are added to.
12614 return this.containerEl;
12617 getButtonContainer : function() /// what children are added to.
12619 return this.el.select(".roo-card-uploader-button-container").first();
12622 initEvents : function()
12625 Roo.bootstrap.Input.prototype.initEvents.call(this);
12629 xns: Roo.bootstrap,
12632 container_method : 'getButtonContainer' ,
12633 html : this.html, // fix changable?
12636 'click' : function(btn, e) {
12645 this.urlAPI = (window.createObjectURL && window) ||
12646 (window.URL && URL.revokeObjectURL && URL) ||
12647 (window.webkitURL && webkitURL);
12652 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12654 this.selectorEl.on('change', this.onFileSelected, this);
12657 this.images.forEach(function(img) {
12660 this.images = false;
12662 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12668 onClick : function(e)
12670 e.preventDefault();
12672 this.selectorEl.dom.click();
12676 onFileSelected : function(e)
12678 e.preventDefault();
12680 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12684 Roo.each(this.selectorEl.dom.files, function(file){
12685 this.addFile(file);
12694 addFile : function(file)
12697 if(typeof(file) === 'string'){
12698 throw "Add file by name?"; // should not happen
12702 if(!file || !this.urlAPI){
12712 var url = _this.urlAPI.createObjectURL( file);
12715 id : Roo.bootstrap.CardUploader.ID--,
12716 is_uploaded : false,
12720 mimetype : file.type,
12728 * addCard - add an Attachment to the uploader
12729 * @param data - the data about the image to upload
12733 title : "Title of file",
12734 is_uploaded : false,
12735 src : "http://.....",
12736 srcfile : { the File upload object },
12737 mimetype : file.type,
12740 .. any other data...
12746 addCard : function (data)
12748 // hidden input element?
12749 // if the file is not an image...
12750 //then we need to use something other that and header_image
12755 xns : Roo.bootstrap,
12756 xtype : 'CardFooter',
12759 xns : Roo.bootstrap,
12765 xns : Roo.bootstrap,
12767 html : String.format("<small>{0}</small>", data.title),
12768 cls : 'col-10 text-left',
12773 click : function() {
12775 t.fireEvent( "download", t, data );
12781 xns : Roo.bootstrap,
12783 style: 'max-height: 28px; ',
12789 click : function() {
12790 t.removeCard(data.id)
12802 var cn = this.addxtype(
12805 xns : Roo.bootstrap,
12808 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12809 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12810 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12815 initEvents : function() {
12816 Roo.bootstrap.Card.prototype.initEvents.call(this);
12818 this.imgEl = this.el.select('.card-img-top').first();
12820 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12821 this.imgEl.set({ 'pointer' : 'cursor' });
12824 this.getCardFooter().addClass('p-1');
12831 // dont' really need ot update items.
12832 // this.items.push(cn);
12833 this.fileCollection.add(cn);
12835 if (!data.srcfile) {
12836 this.updateInput();
12841 var reader = new FileReader();
12842 reader.addEventListener("load", function() {
12843 data.srcdata = reader.result;
12846 reader.readAsDataURL(data.srcfile);
12851 removeCard : function(id)
12854 var card = this.fileCollection.get(id);
12855 card.data.is_deleted = 1;
12856 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12857 //this.fileCollection.remove(card);
12858 //this.items = this.items.filter(function(e) { return e != card });
12859 // dont' really need ot update items.
12860 card.el.dom.parentNode.removeChild(card.el.dom);
12861 this.updateInput();
12867 this.fileCollection.each(function(card) {
12868 if (card.el.dom && card.el.dom.parentNode) {
12869 card.el.dom.parentNode.removeChild(card.el.dom);
12872 this.fileCollection.clear();
12873 this.updateInput();
12876 updateInput : function()
12879 this.fileCollection.each(function(e) {
12883 this.inputEl().dom.value = JSON.stringify(data);
12893 Roo.bootstrap.CardUploader.ID = -1;/*
12895 * Ext JS Library 1.1.1
12896 * Copyright(c) 2006-2007, Ext JS, LLC.
12898 * Originally Released Under LGPL - original licence link has changed is not relivant.
12901 * <script type="text/javascript">
12906 * @class Roo.data.SortTypes
12908 * Defines the default sorting (casting?) comparison functions used when sorting data.
12910 Roo.data.SortTypes = {
12912 * Default sort that does nothing
12913 * @param {Mixed} s The value being converted
12914 * @return {Mixed} The comparison value
12916 none : function(s){
12921 * The regular expression used to strip tags
12925 stripTagsRE : /<\/?[^>]+>/gi,
12928 * Strips all HTML tags to sort on text only
12929 * @param {Mixed} s The value being converted
12930 * @return {String} The comparison value
12932 asText : function(s){
12933 return String(s).replace(this.stripTagsRE, "");
12937 * Strips all HTML tags to sort on text only - Case insensitive
12938 * @param {Mixed} s The value being converted
12939 * @return {String} The comparison value
12941 asUCText : function(s){
12942 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12946 * Case insensitive string
12947 * @param {Mixed} s The value being converted
12948 * @return {String} The comparison value
12950 asUCString : function(s) {
12951 return String(s).toUpperCase();
12956 * @param {Mixed} s The value being converted
12957 * @return {Number} The comparison value
12959 asDate : function(s) {
12963 if(s instanceof Date){
12964 return s.getTime();
12966 return Date.parse(String(s));
12971 * @param {Mixed} s The value being converted
12972 * @return {Float} The comparison value
12974 asFloat : function(s) {
12975 var val = parseFloat(String(s).replace(/,/g, ""));
12984 * @param {Mixed} s The value being converted
12985 * @return {Number} The comparison value
12987 asInt : function(s) {
12988 var val = parseInt(String(s).replace(/,/g, ""));
12996 * Ext JS Library 1.1.1
12997 * Copyright(c) 2006-2007, Ext JS, LLC.
12999 * Originally Released Under LGPL - original licence link has changed is not relivant.
13002 * <script type="text/javascript">
13006 * @class Roo.data.Record
13007 * Instances of this class encapsulate both record <em>definition</em> information, and record
13008 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13009 * to access Records cached in an {@link Roo.data.Store} object.<br>
13011 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13012 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13015 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13017 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13018 * {@link #create}. The parameters are the same.
13019 * @param {Array} data An associative Array of data values keyed by the field name.
13020 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13021 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13022 * not specified an integer id is generated.
13024 Roo.data.Record = function(data, id){
13025 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13030 * Generate a constructor for a specific record layout.
13031 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13032 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13033 * Each field definition object may contain the following properties: <ul>
13034 * <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,
13035 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13036 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13037 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13038 * is being used, then this is a string containing the javascript expression to reference the data relative to
13039 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13040 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13041 * this may be omitted.</p></li>
13042 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13043 * <ul><li>auto (Default, implies no conversion)</li>
13048 * <li>date</li></ul></p></li>
13049 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13050 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13051 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13052 * by the Reader into an object that will be stored in the Record. It is passed the
13053 * following parameters:<ul>
13054 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13056 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13058 * <br>usage:<br><pre><code>
13059 var TopicRecord = Roo.data.Record.create(
13060 {name: 'title', mapping: 'topic_title'},
13061 {name: 'author', mapping: 'username'},
13062 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13063 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13064 {name: 'lastPoster', mapping: 'user2'},
13065 {name: 'excerpt', mapping: 'post_text'}
13068 var myNewRecord = new TopicRecord({
13069 title: 'Do my job please',
13072 lastPost: new Date(),
13073 lastPoster: 'Animal',
13074 excerpt: 'No way dude!'
13076 myStore.add(myNewRecord);
13081 Roo.data.Record.create = function(o){
13082 var f = function(){
13083 f.superclass.constructor.apply(this, arguments);
13085 Roo.extend(f, Roo.data.Record);
13086 var p = f.prototype;
13087 p.fields = new Roo.util.MixedCollection(false, function(field){
13090 for(var i = 0, len = o.length; i < len; i++){
13091 p.fields.add(new Roo.data.Field(o[i]));
13093 f.getField = function(name){
13094 return p.fields.get(name);
13099 Roo.data.Record.AUTO_ID = 1000;
13100 Roo.data.Record.EDIT = 'edit';
13101 Roo.data.Record.REJECT = 'reject';
13102 Roo.data.Record.COMMIT = 'commit';
13104 Roo.data.Record.prototype = {
13106 * Readonly flag - true if this record has been modified.
13115 join : function(store){
13116 this.store = store;
13120 * Set the named field to the specified value.
13121 * @param {String} name The name of the field to set.
13122 * @param {Object} value The value to set the field to.
13124 set : function(name, value){
13125 if(this.data[name] == value){
13129 if(!this.modified){
13130 this.modified = {};
13132 if(typeof this.modified[name] == 'undefined'){
13133 this.modified[name] = this.data[name];
13135 this.data[name] = value;
13136 if(!this.editing && this.store){
13137 this.store.afterEdit(this);
13142 * Get the value of the named field.
13143 * @param {String} name The name of the field to get the value of.
13144 * @return {Object} The value of the field.
13146 get : function(name){
13147 return this.data[name];
13151 beginEdit : function(){
13152 this.editing = true;
13153 this.modified = {};
13157 cancelEdit : function(){
13158 this.editing = false;
13159 delete this.modified;
13163 endEdit : function(){
13164 this.editing = false;
13165 if(this.dirty && this.store){
13166 this.store.afterEdit(this);
13171 * Usually called by the {@link Roo.data.Store} which owns the Record.
13172 * Rejects all changes made to the Record since either creation, or the last commit operation.
13173 * Modified fields are reverted to their original values.
13175 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13176 * of reject operations.
13178 reject : function(){
13179 var m = this.modified;
13181 if(typeof m[n] != "function"){
13182 this.data[n] = m[n];
13185 this.dirty = false;
13186 delete this.modified;
13187 this.editing = false;
13189 this.store.afterReject(this);
13194 * Usually called by the {@link Roo.data.Store} which owns the Record.
13195 * Commits all changes made to the Record since either creation, or the last commit operation.
13197 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13198 * of commit operations.
13200 commit : function(){
13201 this.dirty = false;
13202 delete this.modified;
13203 this.editing = false;
13205 this.store.afterCommit(this);
13210 hasError : function(){
13211 return this.error != null;
13215 clearError : function(){
13220 * Creates a copy of this record.
13221 * @param {String} id (optional) A new record id if you don't want to use this record's id
13224 copy : function(newId) {
13225 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13229 * Ext JS Library 1.1.1
13230 * Copyright(c) 2006-2007, Ext JS, LLC.
13232 * Originally Released Under LGPL - original licence link has changed is not relivant.
13235 * <script type="text/javascript">
13241 * @class Roo.data.Store
13242 * @extends Roo.util.Observable
13243 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13244 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13246 * 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
13247 * has no knowledge of the format of the data returned by the Proxy.<br>
13249 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13250 * instances from the data object. These records are cached and made available through accessor functions.
13252 * Creates a new Store.
13253 * @param {Object} config A config object containing the objects needed for the Store to access data,
13254 * and read the data into Records.
13256 Roo.data.Store = function(config){
13257 this.data = new Roo.util.MixedCollection(false);
13258 this.data.getKey = function(o){
13261 this.baseParams = {};
13263 this.paramNames = {
13268 "multisort" : "_multisort"
13271 if(config && config.data){
13272 this.inlineData = config.data;
13273 delete config.data;
13276 Roo.apply(this, config);
13278 if(this.reader){ // reader passed
13279 this.reader = Roo.factory(this.reader, Roo.data);
13280 this.reader.xmodule = this.xmodule || false;
13281 if(!this.recordType){
13282 this.recordType = this.reader.recordType;
13284 if(this.reader.onMetaChange){
13285 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13289 if(this.recordType){
13290 this.fields = this.recordType.prototype.fields;
13292 this.modified = [];
13296 * @event datachanged
13297 * Fires when the data cache has changed, and a widget which is using this Store
13298 * as a Record cache should refresh its view.
13299 * @param {Store} this
13301 datachanged : true,
13303 * @event metachange
13304 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13305 * @param {Store} this
13306 * @param {Object} meta The JSON metadata
13311 * Fires when Records have been added to the Store
13312 * @param {Store} this
13313 * @param {Roo.data.Record[]} records The array of Records added
13314 * @param {Number} index The index at which the record(s) were added
13319 * Fires when a Record has been removed from the Store
13320 * @param {Store} this
13321 * @param {Roo.data.Record} record The Record that was removed
13322 * @param {Number} index The index at which the record was removed
13327 * Fires when a Record has been updated
13328 * @param {Store} this
13329 * @param {Roo.data.Record} record The Record that was updated
13330 * @param {String} operation The update operation being performed. Value may be one of:
13332 Roo.data.Record.EDIT
13333 Roo.data.Record.REJECT
13334 Roo.data.Record.COMMIT
13340 * Fires when the data cache has been cleared.
13341 * @param {Store} this
13345 * @event beforeload
13346 * Fires before a request is made for a new data object. If the beforeload handler returns false
13347 * the load action will be canceled.
13348 * @param {Store} this
13349 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13353 * @event beforeloadadd
13354 * Fires after a new set of Records has been loaded.
13355 * @param {Store} this
13356 * @param {Roo.data.Record[]} records The Records that were loaded
13357 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13359 beforeloadadd : true,
13362 * Fires after a new set of Records has been loaded, before they are added to the store.
13363 * @param {Store} this
13364 * @param {Roo.data.Record[]} records The Records that were loaded
13365 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13366 * @params {Object} return from reader
13370 * @event loadexception
13371 * Fires if an exception occurs in the Proxy during loading.
13372 * Called with the signature of the Proxy's "loadexception" event.
13373 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13376 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13377 * @param {Object} load options
13378 * @param {Object} jsonData from your request (normally this contains the Exception)
13380 loadexception : true
13384 this.proxy = Roo.factory(this.proxy, Roo.data);
13385 this.proxy.xmodule = this.xmodule || false;
13386 this.relayEvents(this.proxy, ["loadexception"]);
13388 this.sortToggle = {};
13389 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13391 Roo.data.Store.superclass.constructor.call(this);
13393 if(this.inlineData){
13394 this.loadData(this.inlineData);
13395 delete this.inlineData;
13399 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13401 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13402 * without a remote query - used by combo/forms at present.
13406 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13409 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13412 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13413 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13416 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13417 * on any HTTP request
13420 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13423 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13427 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13428 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13430 remoteSort : false,
13433 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13434 * loaded or when a record is removed. (defaults to false).
13436 pruneModifiedRecords : false,
13439 lastOptions : null,
13442 * Add Records to the Store and fires the add event.
13443 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13445 add : function(records){
13446 records = [].concat(records);
13447 for(var i = 0, len = records.length; i < len; i++){
13448 records[i].join(this);
13450 var index = this.data.length;
13451 this.data.addAll(records);
13452 this.fireEvent("add", this, records, index);
13456 * Remove a Record from the Store and fires the remove event.
13457 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13459 remove : function(record){
13460 var index = this.data.indexOf(record);
13461 this.data.removeAt(index);
13463 if(this.pruneModifiedRecords){
13464 this.modified.remove(record);
13466 this.fireEvent("remove", this, record, index);
13470 * Remove all Records from the Store and fires the clear event.
13472 removeAll : function(){
13474 if(this.pruneModifiedRecords){
13475 this.modified = [];
13477 this.fireEvent("clear", this);
13481 * Inserts Records to the Store at the given index and fires the add event.
13482 * @param {Number} index The start index at which to insert the passed Records.
13483 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13485 insert : function(index, records){
13486 records = [].concat(records);
13487 for(var i = 0, len = records.length; i < len; i++){
13488 this.data.insert(index, records[i]);
13489 records[i].join(this);
13491 this.fireEvent("add", this, records, index);
13495 * Get the index within the cache of the passed Record.
13496 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13497 * @return {Number} The index of the passed Record. Returns -1 if not found.
13499 indexOf : function(record){
13500 return this.data.indexOf(record);
13504 * Get the index within the cache of the Record with the passed id.
13505 * @param {String} id The id of the Record to find.
13506 * @return {Number} The index of the Record. Returns -1 if not found.
13508 indexOfId : function(id){
13509 return this.data.indexOfKey(id);
13513 * Get the Record with the specified id.
13514 * @param {String} id The id of the Record to find.
13515 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13517 getById : function(id){
13518 return this.data.key(id);
13522 * Get the Record at the specified index.
13523 * @param {Number} index The index of the Record to find.
13524 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13526 getAt : function(index){
13527 return this.data.itemAt(index);
13531 * Returns a range of Records between specified indices.
13532 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13533 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13534 * @return {Roo.data.Record[]} An array of Records
13536 getRange : function(start, end){
13537 return this.data.getRange(start, end);
13541 storeOptions : function(o){
13542 o = Roo.apply({}, o);
13545 this.lastOptions = o;
13549 * Loads the Record cache from the configured Proxy using the configured Reader.
13551 * If using remote paging, then the first load call must specify the <em>start</em>
13552 * and <em>limit</em> properties in the options.params property to establish the initial
13553 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13555 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13556 * and this call will return before the new data has been loaded. Perform any post-processing
13557 * in a callback function, or in a "load" event handler.</strong>
13559 * @param {Object} options An object containing properties which control loading options:<ul>
13560 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13561 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13562 * passed the following arguments:<ul>
13563 * <li>r : Roo.data.Record[]</li>
13564 * <li>options: Options object from the load call</li>
13565 * <li>success: Boolean success indicator</li></ul></li>
13566 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13567 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13570 load : function(options){
13571 options = options || {};
13572 if(this.fireEvent("beforeload", this, options) !== false){
13573 this.storeOptions(options);
13574 var p = Roo.apply(options.params || {}, this.baseParams);
13575 // if meta was not loaded from remote source.. try requesting it.
13576 if (!this.reader.metaFromRemote) {
13577 p._requestMeta = 1;
13579 if(this.sortInfo && this.remoteSort){
13580 var pn = this.paramNames;
13581 p[pn["sort"]] = this.sortInfo.field;
13582 p[pn["dir"]] = this.sortInfo.direction;
13584 if (this.multiSort) {
13585 var pn = this.paramNames;
13586 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13589 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13594 * Reloads the Record cache from the configured Proxy using the configured Reader and
13595 * the options from the last load operation performed.
13596 * @param {Object} options (optional) An object containing properties which may override the options
13597 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13598 * the most recently used options are reused).
13600 reload : function(options){
13601 this.load(Roo.applyIf(options||{}, this.lastOptions));
13605 // Called as a callback by the Reader during a load operation.
13606 loadRecords : function(o, options, success){
13607 if(!o || success === false){
13608 if(success !== false){
13609 this.fireEvent("load", this, [], options, o);
13611 if(options.callback){
13612 options.callback.call(options.scope || this, [], options, false);
13616 // if data returned failure - throw an exception.
13617 if (o.success === false) {
13618 // show a message if no listener is registered.
13619 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13620 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13622 // loadmask wil be hooked into this..
13623 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13626 var r = o.records, t = o.totalRecords || r.length;
13628 this.fireEvent("beforeloadadd", this, r, options, o);
13630 if(!options || options.add !== true){
13631 if(this.pruneModifiedRecords){
13632 this.modified = [];
13634 for(var i = 0, len = r.length; i < len; i++){
13638 this.data = this.snapshot;
13639 delete this.snapshot;
13642 this.data.addAll(r);
13643 this.totalLength = t;
13645 this.fireEvent("datachanged", this);
13647 this.totalLength = Math.max(t, this.data.length+r.length);
13651 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13653 var e = new Roo.data.Record({});
13655 e.set(this.parent.displayField, this.parent.emptyTitle);
13656 e.set(this.parent.valueField, '');
13661 this.fireEvent("load", this, r, options, o);
13662 if(options.callback){
13663 options.callback.call(options.scope || this, r, options, true);
13669 * Loads data from a passed data block. A Reader which understands the format of the data
13670 * must have been configured in the constructor.
13671 * @param {Object} data The data block from which to read the Records. The format of the data expected
13672 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13673 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13675 loadData : function(o, append){
13676 var r = this.reader.readRecords(o);
13677 this.loadRecords(r, {add: append}, true);
13681 * using 'cn' the nested child reader read the child array into it's child stores.
13682 * @param {Object} rec The record with a 'children array
13684 loadDataFromChildren : function(rec)
13686 this.loadData(this.reader.toLoadData(rec));
13691 * Gets the number of cached records.
13693 * <em>If using paging, this may not be the total size of the dataset. If the data object
13694 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13695 * the data set size</em>
13697 getCount : function(){
13698 return this.data.length || 0;
13702 * Gets the total number of records in the dataset as returned by the server.
13704 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13705 * the dataset size</em>
13707 getTotalCount : function(){
13708 return this.totalLength || 0;
13712 * Returns the sort state of the Store as an object with two properties:
13714 field {String} The name of the field by which the Records are sorted
13715 direction {String} The sort order, "ASC" or "DESC"
13718 getSortState : function(){
13719 return this.sortInfo;
13723 applySort : function(){
13724 if(this.sortInfo && !this.remoteSort){
13725 var s = this.sortInfo, f = s.field;
13726 var st = this.fields.get(f).sortType;
13727 var fn = function(r1, r2){
13728 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13729 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13731 this.data.sort(s.direction, fn);
13732 if(this.snapshot && this.snapshot != this.data){
13733 this.snapshot.sort(s.direction, fn);
13739 * Sets the default sort column and order to be used by the next load operation.
13740 * @param {String} fieldName The name of the field to sort by.
13741 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13743 setDefaultSort : function(field, dir){
13744 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13748 * Sort the Records.
13749 * If remote sorting is used, the sort is performed on the server, and the cache is
13750 * reloaded. If local sorting is used, the cache is sorted internally.
13751 * @param {String} fieldName The name of the field to sort by.
13752 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13754 sort : function(fieldName, dir){
13755 var f = this.fields.get(fieldName);
13757 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13759 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13760 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13765 this.sortToggle[f.name] = dir;
13766 this.sortInfo = {field: f.name, direction: dir};
13767 if(!this.remoteSort){
13769 this.fireEvent("datachanged", this);
13771 this.load(this.lastOptions);
13776 * Calls the specified function for each of the Records in the cache.
13777 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13778 * Returning <em>false</em> aborts and exits the iteration.
13779 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13781 each : function(fn, scope){
13782 this.data.each(fn, scope);
13786 * Gets all records modified since the last commit. Modified records are persisted across load operations
13787 * (e.g., during paging).
13788 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13790 getModifiedRecords : function(){
13791 return this.modified;
13795 createFilterFn : function(property, value, anyMatch){
13796 if(!value.exec){ // not a regex
13797 value = String(value);
13798 if(value.length == 0){
13801 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13803 return function(r){
13804 return value.test(r.data[property]);
13809 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13810 * @param {String} property A field on your records
13811 * @param {Number} start The record index to start at (defaults to 0)
13812 * @param {Number} end The last record index to include (defaults to length - 1)
13813 * @return {Number} The sum
13815 sum : function(property, start, end){
13816 var rs = this.data.items, v = 0;
13817 start = start || 0;
13818 end = (end || end === 0) ? end : rs.length-1;
13820 for(var i = start; i <= end; i++){
13821 v += (rs[i].data[property] || 0);
13827 * Filter the records by a specified property.
13828 * @param {String} field A field on your records
13829 * @param {String/RegExp} value Either a string that the field
13830 * should start with or a RegExp to test against the field
13831 * @param {Boolean} anyMatch True to match any part not just the beginning
13833 filter : function(property, value, anyMatch){
13834 var fn = this.createFilterFn(property, value, anyMatch);
13835 return fn ? this.filterBy(fn) : this.clearFilter();
13839 * Filter by a function. The specified function will be called with each
13840 * record in this data source. If the function returns true the record is included,
13841 * otherwise it is filtered.
13842 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13843 * @param {Object} scope (optional) The scope of the function (defaults to this)
13845 filterBy : function(fn, scope){
13846 this.snapshot = this.snapshot || this.data;
13847 this.data = this.queryBy(fn, scope||this);
13848 this.fireEvent("datachanged", this);
13852 * Query the records by a specified property.
13853 * @param {String} field A field on your records
13854 * @param {String/RegExp} value Either a string that the field
13855 * should start with or a RegExp to test against the field
13856 * @param {Boolean} anyMatch True to match any part not just the beginning
13857 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13859 query : function(property, value, anyMatch){
13860 var fn = this.createFilterFn(property, value, anyMatch);
13861 return fn ? this.queryBy(fn) : this.data.clone();
13865 * Query by a function. The specified function will be called with each
13866 * record in this data source. If the function returns true the record is included
13868 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13869 * @param {Object} scope (optional) The scope of the function (defaults to this)
13870 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13872 queryBy : function(fn, scope){
13873 var data = this.snapshot || this.data;
13874 return data.filterBy(fn, scope||this);
13878 * Collects unique values for a particular dataIndex from this store.
13879 * @param {String} dataIndex The property to collect
13880 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13881 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13882 * @return {Array} An array of the unique values
13884 collect : function(dataIndex, allowNull, bypassFilter){
13885 var d = (bypassFilter === true && this.snapshot) ?
13886 this.snapshot.items : this.data.items;
13887 var v, sv, r = [], l = {};
13888 for(var i = 0, len = d.length; i < len; i++){
13889 v = d[i].data[dataIndex];
13891 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13900 * Revert to a view of the Record cache with no filtering applied.
13901 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13903 clearFilter : function(suppressEvent){
13904 if(this.snapshot && this.snapshot != this.data){
13905 this.data = this.snapshot;
13906 delete this.snapshot;
13907 if(suppressEvent !== true){
13908 this.fireEvent("datachanged", this);
13914 afterEdit : function(record){
13915 if(this.modified.indexOf(record) == -1){
13916 this.modified.push(record);
13918 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13922 afterReject : function(record){
13923 this.modified.remove(record);
13924 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13928 afterCommit : function(record){
13929 this.modified.remove(record);
13930 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13934 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13935 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13937 commitChanges : function(){
13938 var m = this.modified.slice(0);
13939 this.modified = [];
13940 for(var i = 0, len = m.length; i < len; i++){
13946 * Cancel outstanding changes on all changed records.
13948 rejectChanges : function(){
13949 var m = this.modified.slice(0);
13950 this.modified = [];
13951 for(var i = 0, len = m.length; i < len; i++){
13956 onMetaChange : function(meta, rtype, o){
13957 this.recordType = rtype;
13958 this.fields = rtype.prototype.fields;
13959 delete this.snapshot;
13960 this.sortInfo = meta.sortInfo || this.sortInfo;
13961 this.modified = [];
13962 this.fireEvent('metachange', this, this.reader.meta);
13965 moveIndex : function(data, type)
13967 var index = this.indexOf(data);
13969 var newIndex = index + type;
13973 this.insert(newIndex, data);
13978 * Ext JS Library 1.1.1
13979 * Copyright(c) 2006-2007, Ext JS, LLC.
13981 * Originally Released Under LGPL - original licence link has changed is not relivant.
13984 * <script type="text/javascript">
13988 * @class Roo.data.SimpleStore
13989 * @extends Roo.data.Store
13990 * Small helper class to make creating Stores from Array data easier.
13991 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13992 * @cfg {Array} fields An array of field definition objects, or field name strings.
13993 * @cfg {Object} an existing reader (eg. copied from another store)
13994 * @cfg {Array} data The multi-dimensional array of data
13996 * @param {Object} config
13998 Roo.data.SimpleStore = function(config)
14000 Roo.data.SimpleStore.superclass.constructor.call(this, {
14002 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14005 Roo.data.Record.create(config.fields)
14007 proxy : new Roo.data.MemoryProxy(config.data)
14011 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14013 * Ext JS Library 1.1.1
14014 * Copyright(c) 2006-2007, Ext JS, LLC.
14016 * Originally Released Under LGPL - original licence link has changed is not relivant.
14019 * <script type="text/javascript">
14024 * @extends Roo.data.Store
14025 * @class Roo.data.JsonStore
14026 * Small helper class to make creating Stores for JSON data easier. <br/>
14028 var store = new Roo.data.JsonStore({
14029 url: 'get-images.php',
14031 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14034 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14035 * JsonReader and HttpProxy (unless inline data is provided).</b>
14036 * @cfg {Array} fields An array of field definition objects, or field name strings.
14038 * @param {Object} config
14040 Roo.data.JsonStore = function(c){
14041 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14042 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14043 reader: new Roo.data.JsonReader(c, c.fields)
14046 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14048 * Ext JS Library 1.1.1
14049 * Copyright(c) 2006-2007, Ext JS, LLC.
14051 * Originally Released Under LGPL - original licence link has changed is not relivant.
14054 * <script type="text/javascript">
14058 Roo.data.Field = function(config){
14059 if(typeof config == "string"){
14060 config = {name: config};
14062 Roo.apply(this, config);
14065 this.type = "auto";
14068 var st = Roo.data.SortTypes;
14069 // named sortTypes are supported, here we look them up
14070 if(typeof this.sortType == "string"){
14071 this.sortType = st[this.sortType];
14074 // set default sortType for strings and dates
14075 if(!this.sortType){
14078 this.sortType = st.asUCString;
14081 this.sortType = st.asDate;
14084 this.sortType = st.none;
14089 var stripRe = /[\$,%]/g;
14091 // prebuilt conversion function for this field, instead of
14092 // switching every time we're reading a value
14094 var cv, dateFormat = this.dateFormat;
14099 cv = function(v){ return v; };
14102 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14106 return v !== undefined && v !== null && v !== '' ?
14107 parseInt(String(v).replace(stripRe, ""), 10) : '';
14112 return v !== undefined && v !== null && v !== '' ?
14113 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14118 cv = function(v){ return v === true || v === "true" || v == 1; };
14125 if(v instanceof Date){
14129 if(dateFormat == "timestamp"){
14130 return new Date(v*1000);
14132 return Date.parseDate(v, dateFormat);
14134 var parsed = Date.parse(v);
14135 return parsed ? new Date(parsed) : null;
14144 Roo.data.Field.prototype = {
14152 * Ext JS Library 1.1.1
14153 * Copyright(c) 2006-2007, Ext JS, LLC.
14155 * Originally Released Under LGPL - original licence link has changed is not relivant.
14158 * <script type="text/javascript">
14161 // Base class for reading structured data from a data source. This class is intended to be
14162 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14165 * @class Roo.data.DataReader
14166 * Base class for reading structured data from a data source. This class is intended to be
14167 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14170 Roo.data.DataReader = function(meta, recordType){
14174 this.recordType = recordType instanceof Array ?
14175 Roo.data.Record.create(recordType) : recordType;
14178 Roo.data.DataReader.prototype = {
14181 readerType : 'Data',
14183 * Create an empty record
14184 * @param {Object} data (optional) - overlay some values
14185 * @return {Roo.data.Record} record created.
14187 newRow : function(d) {
14189 this.recordType.prototype.fields.each(function(c) {
14191 case 'int' : da[c.name] = 0; break;
14192 case 'date' : da[c.name] = new Date(); break;
14193 case 'float' : da[c.name] = 0.0; break;
14194 case 'boolean' : da[c.name] = false; break;
14195 default : da[c.name] = ""; break;
14199 return new this.recordType(Roo.apply(da, d));
14205 * Ext JS Library 1.1.1
14206 * Copyright(c) 2006-2007, Ext JS, LLC.
14208 * Originally Released Under LGPL - original licence link has changed is not relivant.
14211 * <script type="text/javascript">
14215 * @class Roo.data.DataProxy
14216 * @extends Roo.data.Observable
14217 * This class is an abstract base class for implementations which provide retrieval of
14218 * unformatted data objects.<br>
14220 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14221 * (of the appropriate type which knows how to parse the data object) to provide a block of
14222 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14224 * Custom implementations must implement the load method as described in
14225 * {@link Roo.data.HttpProxy#load}.
14227 Roo.data.DataProxy = function(){
14230 * @event beforeload
14231 * Fires before a network request is made to retrieve a data object.
14232 * @param {Object} This DataProxy object.
14233 * @param {Object} params The params parameter to the load function.
14238 * Fires before the load method's callback is called.
14239 * @param {Object} This DataProxy object.
14240 * @param {Object} o The data object.
14241 * @param {Object} arg The callback argument object passed to the load function.
14245 * @event loadexception
14246 * Fires if an Exception occurs during data retrieval.
14247 * @param {Object} This DataProxy object.
14248 * @param {Object} o The data object.
14249 * @param {Object} arg The callback argument object passed to the load function.
14250 * @param {Object} e The Exception.
14252 loadexception : true
14254 Roo.data.DataProxy.superclass.constructor.call(this);
14257 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14260 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14264 * Ext JS Library 1.1.1
14265 * Copyright(c) 2006-2007, Ext JS, LLC.
14267 * Originally Released Under LGPL - original licence link has changed is not relivant.
14270 * <script type="text/javascript">
14273 * @class Roo.data.MemoryProxy
14274 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14275 * to the Reader when its load method is called.
14277 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14279 Roo.data.MemoryProxy = function(data){
14283 Roo.data.MemoryProxy.superclass.constructor.call(this);
14287 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14290 * Load data from the requested source (in this case an in-memory
14291 * data object passed to the constructor), read the data object into
14292 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14293 * process that block using the passed callback.
14294 * @param {Object} params This parameter is not used by the MemoryProxy class.
14295 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14296 * object into a block of Roo.data.Records.
14297 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14298 * The function must be passed <ul>
14299 * <li>The Record block object</li>
14300 * <li>The "arg" argument from the load function</li>
14301 * <li>A boolean success indicator</li>
14303 * @param {Object} scope The scope in which to call the callback
14304 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14306 load : function(params, reader, callback, scope, arg){
14307 params = params || {};
14310 result = reader.readRecords(params.data ? params.data :this.data);
14312 this.fireEvent("loadexception", this, arg, null, e);
14313 callback.call(scope, null, arg, false);
14316 callback.call(scope, result, arg, true);
14320 update : function(params, records){
14325 * Ext JS Library 1.1.1
14326 * Copyright(c) 2006-2007, Ext JS, LLC.
14328 * Originally Released Under LGPL - original licence link has changed is not relivant.
14331 * <script type="text/javascript">
14334 * @class Roo.data.HttpProxy
14335 * @extends Roo.data.DataProxy
14336 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14337 * configured to reference a certain URL.<br><br>
14339 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14340 * from which the running page was served.<br><br>
14342 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14344 * Be aware that to enable the browser to parse an XML document, the server must set
14345 * the Content-Type header in the HTTP response to "text/xml".
14347 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14348 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14349 * will be used to make the request.
14351 Roo.data.HttpProxy = function(conn){
14352 Roo.data.HttpProxy.superclass.constructor.call(this);
14353 // is conn a conn config or a real conn?
14355 this.useAjax = !conn || !conn.events;
14359 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14360 // thse are take from connection...
14363 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14366 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14367 * extra parameters to each request made by this object. (defaults to undefined)
14370 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14371 * to each request made by this object. (defaults to undefined)
14374 * @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)
14377 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14380 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14386 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14390 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14391 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14392 * a finer-grained basis than the DataProxy events.
14394 getConnection : function(){
14395 return this.useAjax ? Roo.Ajax : this.conn;
14399 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14400 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14401 * process that block using the passed callback.
14402 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14403 * for the request to the remote server.
14404 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14405 * object into a block of Roo.data.Records.
14406 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14407 * The function must be passed <ul>
14408 * <li>The Record block object</li>
14409 * <li>The "arg" argument from the load function</li>
14410 * <li>A boolean success indicator</li>
14412 * @param {Object} scope The scope in which to call the callback
14413 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14415 load : function(params, reader, callback, scope, arg){
14416 if(this.fireEvent("beforeload", this, params) !== false){
14418 params : params || {},
14420 callback : callback,
14425 callback : this.loadResponse,
14429 Roo.applyIf(o, this.conn);
14430 if(this.activeRequest){
14431 Roo.Ajax.abort(this.activeRequest);
14433 this.activeRequest = Roo.Ajax.request(o);
14435 this.conn.request(o);
14438 callback.call(scope||this, null, arg, false);
14443 loadResponse : function(o, success, response){
14444 delete this.activeRequest;
14446 this.fireEvent("loadexception", this, o, response);
14447 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14452 result = o.reader.read(response);
14454 this.fireEvent("loadexception", this, o, response, e);
14455 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14459 this.fireEvent("load", this, o, o.request.arg);
14460 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14464 update : function(dataSet){
14469 updateResponse : function(dataSet){
14474 * Ext JS Library 1.1.1
14475 * Copyright(c) 2006-2007, Ext JS, LLC.
14477 * Originally Released Under LGPL - original licence link has changed is not relivant.
14480 * <script type="text/javascript">
14484 * @class Roo.data.ScriptTagProxy
14485 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14486 * other than the originating domain of the running page.<br><br>
14488 * <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
14489 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14491 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14492 * source code that is used as the source inside a <script> tag.<br><br>
14494 * In order for the browser to process the returned data, the server must wrap the data object
14495 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14496 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14497 * depending on whether the callback name was passed:
14500 boolean scriptTag = false;
14501 String cb = request.getParameter("callback");
14504 response.setContentType("text/javascript");
14506 response.setContentType("application/x-json");
14508 Writer out = response.getWriter();
14510 out.write(cb + "(");
14512 out.print(dataBlock.toJsonString());
14519 * @param {Object} config A configuration object.
14521 Roo.data.ScriptTagProxy = function(config){
14522 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14523 Roo.apply(this, config);
14524 this.head = document.getElementsByTagName("head")[0];
14527 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14529 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14531 * @cfg {String} url The URL from which to request the data object.
14534 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14538 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14539 * the server the name of the callback function set up by the load call to process the returned data object.
14540 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14541 * javascript output which calls this named function passing the data object as its only parameter.
14543 callbackParam : "callback",
14545 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14546 * name to the request.
14551 * Load data from the configured URL, read the data object into
14552 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14553 * process that block using the passed callback.
14554 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14555 * for the request to the remote server.
14556 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14557 * object into a block of Roo.data.Records.
14558 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14559 * The function must be passed <ul>
14560 * <li>The Record block object</li>
14561 * <li>The "arg" argument from the load function</li>
14562 * <li>A boolean success indicator</li>
14564 * @param {Object} scope The scope in which to call the callback
14565 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14567 load : function(params, reader, callback, scope, arg){
14568 if(this.fireEvent("beforeload", this, params) !== false){
14570 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14572 var url = this.url;
14573 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14575 url += "&_dc=" + (new Date().getTime());
14577 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14580 cb : "stcCallback"+transId,
14581 scriptId : "stcScript"+transId,
14585 callback : callback,
14591 window[trans.cb] = function(o){
14592 conn.handleResponse(o, trans);
14595 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14597 if(this.autoAbort !== false){
14601 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14603 var script = document.createElement("script");
14604 script.setAttribute("src", url);
14605 script.setAttribute("type", "text/javascript");
14606 script.setAttribute("id", trans.scriptId);
14607 this.head.appendChild(script);
14609 this.trans = trans;
14611 callback.call(scope||this, null, arg, false);
14616 isLoading : function(){
14617 return this.trans ? true : false;
14621 * Abort the current server request.
14623 abort : function(){
14624 if(this.isLoading()){
14625 this.destroyTrans(this.trans);
14630 destroyTrans : function(trans, isLoaded){
14631 this.head.removeChild(document.getElementById(trans.scriptId));
14632 clearTimeout(trans.timeoutId);
14634 window[trans.cb] = undefined;
14636 delete window[trans.cb];
14639 // if hasn't been loaded, wait for load to remove it to prevent script error
14640 window[trans.cb] = function(){
14641 window[trans.cb] = undefined;
14643 delete window[trans.cb];
14650 handleResponse : function(o, trans){
14651 this.trans = false;
14652 this.destroyTrans(trans, true);
14655 result = trans.reader.readRecords(o);
14657 this.fireEvent("loadexception", this, o, trans.arg, e);
14658 trans.callback.call(trans.scope||window, null, trans.arg, false);
14661 this.fireEvent("load", this, o, trans.arg);
14662 trans.callback.call(trans.scope||window, result, trans.arg, true);
14666 handleFailure : function(trans){
14667 this.trans = false;
14668 this.destroyTrans(trans, false);
14669 this.fireEvent("loadexception", this, null, trans.arg);
14670 trans.callback.call(trans.scope||window, null, trans.arg, false);
14674 * Ext JS Library 1.1.1
14675 * Copyright(c) 2006-2007, Ext JS, LLC.
14677 * Originally Released Under LGPL - original licence link has changed is not relivant.
14680 * <script type="text/javascript">
14684 * @class Roo.data.JsonReader
14685 * @extends Roo.data.DataReader
14686 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14687 * based on mappings in a provided Roo.data.Record constructor.
14689 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14690 * in the reply previously.
14695 var RecordDef = Roo.data.Record.create([
14696 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14697 {name: 'occupation'} // This field will use "occupation" as the mapping.
14699 var myReader = new Roo.data.JsonReader({
14700 totalProperty: "results", // The property which contains the total dataset size (optional)
14701 root: "rows", // The property which contains an Array of row objects
14702 id: "id" // The property within each row object that provides an ID for the record (optional)
14706 * This would consume a JSON file like this:
14708 { 'results': 2, 'rows': [
14709 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14710 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14713 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14714 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14715 * paged from the remote server.
14716 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14717 * @cfg {String} root name of the property which contains the Array of row objects.
14718 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14719 * @cfg {Array} fields Array of field definition objects
14721 * Create a new JsonReader
14722 * @param {Object} meta Metadata configuration options
14723 * @param {Object} recordType Either an Array of field definition objects,
14724 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14726 Roo.data.JsonReader = function(meta, recordType){
14729 // set some defaults:
14730 Roo.applyIf(meta, {
14731 totalProperty: 'total',
14732 successProperty : 'success',
14737 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14739 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14741 readerType : 'Json',
14744 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14745 * Used by Store query builder to append _requestMeta to params.
14748 metaFromRemote : false,
14750 * This method is only used by a DataProxy which has retrieved data from a remote server.
14751 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14752 * @return {Object} data A data block which is used by an Roo.data.Store object as
14753 * a cache of Roo.data.Records.
14755 read : function(response){
14756 var json = response.responseText;
14758 var o = /* eval:var:o */ eval("("+json+")");
14760 throw {message: "JsonReader.read: Json object not found"};
14766 this.metaFromRemote = true;
14767 this.meta = o.metaData;
14768 this.recordType = Roo.data.Record.create(o.metaData.fields);
14769 this.onMetaChange(this.meta, this.recordType, o);
14771 return this.readRecords(o);
14774 // private function a store will implement
14775 onMetaChange : function(meta, recordType, o){
14782 simpleAccess: function(obj, subsc) {
14789 getJsonAccessor: function(){
14791 return function(expr) {
14793 return(re.test(expr))
14794 ? new Function("obj", "return obj." + expr)
14799 return Roo.emptyFn;
14804 * Create a data block containing Roo.data.Records from an XML document.
14805 * @param {Object} o An object which contains an Array of row objects in the property specified
14806 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14807 * which contains the total size of the dataset.
14808 * @return {Object} data A data block which is used by an Roo.data.Store object as
14809 * a cache of Roo.data.Records.
14811 readRecords : function(o){
14813 * After any data loads, the raw JSON data is available for further custom processing.
14817 var s = this.meta, Record = this.recordType,
14818 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14820 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14822 if(s.totalProperty) {
14823 this.getTotal = this.getJsonAccessor(s.totalProperty);
14825 if(s.successProperty) {
14826 this.getSuccess = this.getJsonAccessor(s.successProperty);
14828 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14830 var g = this.getJsonAccessor(s.id);
14831 this.getId = function(rec) {
14833 return (r === undefined || r === "") ? null : r;
14836 this.getId = function(){return null;};
14839 for(var jj = 0; jj < fl; jj++){
14841 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14842 this.ef[jj] = this.getJsonAccessor(map);
14846 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14847 if(s.totalProperty){
14848 var vt = parseInt(this.getTotal(o), 10);
14853 if(s.successProperty){
14854 var vs = this.getSuccess(o);
14855 if(vs === false || vs === 'false'){
14860 for(var i = 0; i < c; i++){
14863 var id = this.getId(n);
14864 for(var j = 0; j < fl; j++){
14866 var v = this.ef[j](n);
14868 Roo.log('missing convert for ' + f.name);
14872 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14874 var record = new Record(values, id);
14876 records[i] = record;
14882 totalRecords : totalRecords
14885 // used when loading children.. @see loadDataFromChildren
14886 toLoadData: function(rec)
14888 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14889 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14890 return { data : data, total : data.length };
14895 * Ext JS Library 1.1.1
14896 * Copyright(c) 2006-2007, Ext JS, LLC.
14898 * Originally Released Under LGPL - original licence link has changed is not relivant.
14901 * <script type="text/javascript">
14905 * @class Roo.data.ArrayReader
14906 * @extends Roo.data.DataReader
14907 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14908 * Each element of that Array represents a row of data fields. The
14909 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14910 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14914 var RecordDef = Roo.data.Record.create([
14915 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14916 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14918 var myReader = new Roo.data.ArrayReader({
14919 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14923 * This would consume an Array like this:
14925 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14929 * Create a new JsonReader
14930 * @param {Object} meta Metadata configuration options.
14931 * @param {Object|Array} recordType Either an Array of field definition objects
14933 * @cfg {Array} fields Array of field definition objects
14934 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14935 * as specified to {@link Roo.data.Record#create},
14936 * or an {@link Roo.data.Record} object
14939 * created using {@link Roo.data.Record#create}.
14941 Roo.data.ArrayReader = function(meta, recordType)
14943 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14946 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14949 * Create a data block containing Roo.data.Records from an XML document.
14950 * @param {Object} o An Array of row objects which represents the dataset.
14951 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14952 * a cache of Roo.data.Records.
14954 readRecords : function(o)
14956 var sid = this.meta ? this.meta.id : null;
14957 var recordType = this.recordType, fields = recordType.prototype.fields;
14960 for(var i = 0; i < root.length; i++){
14963 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14964 for(var j = 0, jlen = fields.length; j < jlen; j++){
14965 var f = fields.items[j];
14966 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14967 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14969 values[f.name] = v;
14971 var record = new recordType(values, id);
14973 records[records.length] = record;
14977 totalRecords : records.length
14980 // used when loading children.. @see loadDataFromChildren
14981 toLoadData: function(rec)
14983 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14984 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14995 * @class Roo.bootstrap.ComboBox
14996 * @extends Roo.bootstrap.TriggerField
14997 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14998 * @cfg {Boolean} append (true|false) default false
14999 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15000 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15001 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15002 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15003 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15004 * @cfg {Boolean} animate default true
15005 * @cfg {Boolean} emptyResultText only for touch device
15006 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15007 * @cfg {String} emptyTitle default ''
15008 * @cfg {Number} width fixed with? experimental
15010 * Create a new ComboBox.
15011 * @param {Object} config Configuration options
15013 Roo.bootstrap.ComboBox = function(config){
15014 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15018 * Fires when the dropdown list is expanded
15019 * @param {Roo.bootstrap.ComboBox} combo This combo box
15024 * Fires when the dropdown list is collapsed
15025 * @param {Roo.bootstrap.ComboBox} combo This combo box
15029 * @event beforeselect
15030 * Fires before a list item is selected. Return false to cancel the selection.
15031 * @param {Roo.bootstrap.ComboBox} combo This combo box
15032 * @param {Roo.data.Record} record The data record returned from the underlying store
15033 * @param {Number} index The index of the selected item in the dropdown list
15035 'beforeselect' : true,
15038 * Fires when a list item is selected
15039 * @param {Roo.bootstrap.ComboBox} combo This combo box
15040 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15041 * @param {Number} index The index of the selected item in the dropdown list
15045 * @event beforequery
15046 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15047 * The event object passed has these properties:
15048 * @param {Roo.bootstrap.ComboBox} combo This combo box
15049 * @param {String} query The query
15050 * @param {Boolean} forceAll true to force "all" query
15051 * @param {Boolean} cancel true to cancel the query
15052 * @param {Object} e The query event object
15054 'beforequery': true,
15057 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15058 * @param {Roo.bootstrap.ComboBox} combo This combo box
15063 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15064 * @param {Roo.bootstrap.ComboBox} combo This combo box
15065 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15070 * Fires when the remove value from the combobox array
15071 * @param {Roo.bootstrap.ComboBox} combo This combo box
15075 * @event afterremove
15076 * Fires when the remove value from the combobox array
15077 * @param {Roo.bootstrap.ComboBox} combo This combo box
15079 'afterremove' : true,
15081 * @event specialfilter
15082 * Fires when specialfilter
15083 * @param {Roo.bootstrap.ComboBox} combo This combo box
15085 'specialfilter' : true,
15088 * Fires when tick the element
15089 * @param {Roo.bootstrap.ComboBox} combo This combo box
15093 * @event touchviewdisplay
15094 * Fires when touch view require special display (default is using displayField)
15095 * @param {Roo.bootstrap.ComboBox} combo This combo box
15096 * @param {Object} cfg set html .
15098 'touchviewdisplay' : true
15103 this.tickItems = [];
15105 this.selectedIndex = -1;
15106 if(this.mode == 'local'){
15107 if(config.queryDelay === undefined){
15108 this.queryDelay = 10;
15110 if(config.minChars === undefined){
15116 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15119 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15120 * rendering into an Roo.Editor, defaults to false)
15123 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15124 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15127 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15130 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15131 * the dropdown list (defaults to undefined, with no header element)
15135 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15139 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15141 listWidth: undefined,
15143 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15144 * mode = 'remote' or 'text' if mode = 'local')
15146 displayField: undefined,
15149 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15150 * mode = 'remote' or 'value' if mode = 'local').
15151 * Note: use of a valueField requires the user make a selection
15152 * in order for a value to be mapped.
15154 valueField: undefined,
15156 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15161 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15162 * field's data value (defaults to the underlying DOM element's name)
15164 hiddenName: undefined,
15166 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15170 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15172 selectedClass: 'active',
15175 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15179 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15180 * anchor positions (defaults to 'tl-bl')
15182 listAlign: 'tl-bl?',
15184 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15188 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15189 * query specified by the allQuery config option (defaults to 'query')
15191 triggerAction: 'query',
15193 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15194 * (defaults to 4, does not apply if editable = false)
15198 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15199 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15203 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15204 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15208 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15209 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15213 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15214 * when editable = true (defaults to false)
15216 selectOnFocus:false,
15218 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15220 queryParam: 'query',
15222 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15223 * when mode = 'remote' (defaults to 'Loading...')
15225 loadingText: 'Loading...',
15227 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15231 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15235 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15236 * traditional select (defaults to true)
15240 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15244 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15248 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15249 * listWidth has a higher value)
15253 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15254 * allow the user to set arbitrary text into the field (defaults to false)
15256 forceSelection:false,
15258 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15259 * if typeAhead = true (defaults to 250)
15261 typeAheadDelay : 250,
15263 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15264 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15266 valueNotFoundText : undefined,
15268 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15270 blockFocus : false,
15273 * @cfg {Boolean} disableClear Disable showing of clear button.
15275 disableClear : false,
15277 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15279 alwaysQuery : false,
15282 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15287 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15289 invalidClass : "has-warning",
15292 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15294 validClass : "has-success",
15297 * @cfg {Boolean} specialFilter (true|false) special filter default false
15299 specialFilter : false,
15302 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15304 mobileTouchView : true,
15307 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15309 useNativeIOS : false,
15312 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15314 mobile_restrict_height : false,
15316 ios_options : false,
15328 btnPosition : 'right',
15329 triggerList : true,
15330 showToggleBtn : true,
15332 emptyResultText: 'Empty',
15333 triggerText : 'Select',
15337 // element that contains real text value.. (when hidden is used..)
15339 getAutoCreate : function()
15344 * Render classic select for iso
15347 if(Roo.isIOS && this.useNativeIOS){
15348 cfg = this.getAutoCreateNativeIOS();
15356 if(Roo.isTouch && this.mobileTouchView){
15357 cfg = this.getAutoCreateTouchView();
15364 if(!this.tickable){
15365 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15370 * ComboBox with tickable selections
15373 var align = this.labelAlign || this.parentLabelAlign();
15376 cls : 'form-group roo-combobox-tickable' //input-group
15379 var btn_text_select = '';
15380 var btn_text_done = '';
15381 var btn_text_cancel = '';
15383 if (this.btn_text_show) {
15384 btn_text_select = 'Select';
15385 btn_text_done = 'Done';
15386 btn_text_cancel = 'Cancel';
15391 cls : 'tickable-buttons',
15396 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15397 //html : this.triggerText
15398 html: btn_text_select
15404 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15406 html: btn_text_done
15412 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15414 html: btn_text_cancel
15420 buttons.cn.unshift({
15422 cls: 'roo-select2-search-field-input'
15428 Roo.each(buttons.cn, function(c){
15430 c.cls += ' btn-' + _this.size;
15433 if (_this.disabled) {
15440 style : 'display: contents',
15445 cls: 'form-hidden-field'
15449 cls: 'roo-select2-choices',
15453 cls: 'roo-select2-search-field',
15464 cls: 'roo-select2-container input-group roo-select2-container-multi',
15470 // cls: 'typeahead typeahead-long dropdown-menu',
15471 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15476 if(this.hasFeedback && !this.allowBlank){
15480 cls: 'glyphicon form-control-feedback'
15483 combobox.cn.push(feedback);
15490 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15491 tooltip : 'This field is required'
15493 if (Roo.bootstrap.version == 4) {
15496 style : 'display:none'
15499 if (align ==='left' && this.fieldLabel.length) {
15501 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15508 cls : 'control-label col-form-label',
15509 html : this.fieldLabel
15521 var labelCfg = cfg.cn[1];
15522 var contentCfg = cfg.cn[2];
15525 if(this.indicatorpos == 'right'){
15531 cls : 'control-label col-form-label',
15535 html : this.fieldLabel
15551 labelCfg = cfg.cn[0];
15552 contentCfg = cfg.cn[1];
15556 if(this.labelWidth > 12){
15557 labelCfg.style = "width: " + this.labelWidth + 'px';
15559 if(this.width * 1 > 0){
15560 contentCfg.style = "width: " + this.width + 'px';
15562 if(this.labelWidth < 13 && this.labelmd == 0){
15563 this.labelmd = this.labelWidth;
15566 if(this.labellg > 0){
15567 labelCfg.cls += ' col-lg-' + this.labellg;
15568 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15571 if(this.labelmd > 0){
15572 labelCfg.cls += ' col-md-' + this.labelmd;
15573 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15576 if(this.labelsm > 0){
15577 labelCfg.cls += ' col-sm-' + this.labelsm;
15578 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15581 if(this.labelxs > 0){
15582 labelCfg.cls += ' col-xs-' + this.labelxs;
15583 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15587 } else if ( this.fieldLabel.length) {
15588 // Roo.log(" label");
15593 //cls : 'input-group-addon',
15594 html : this.fieldLabel
15599 if(this.indicatorpos == 'right'){
15603 //cls : 'input-group-addon',
15604 html : this.fieldLabel
15614 // Roo.log(" no label && no align");
15621 ['xs','sm','md','lg'].map(function(size){
15622 if (settings[size]) {
15623 cfg.cls += ' col-' + size + '-' + settings[size];
15631 _initEventsCalled : false,
15634 initEvents: function()
15636 if (this._initEventsCalled) { // as we call render... prevent looping...
15639 this._initEventsCalled = true;
15642 throw "can not find store for combo";
15645 this.indicator = this.indicatorEl();
15647 this.store = Roo.factory(this.store, Roo.data);
15648 this.store.parent = this;
15650 // if we are building from html. then this element is so complex, that we can not really
15651 // use the rendered HTML.
15652 // so we have to trash and replace the previous code.
15653 if (Roo.XComponent.build_from_html) {
15654 // remove this element....
15655 var e = this.el.dom, k=0;
15656 while (e ) { e = e.previousSibling; ++k;}
15661 this.rendered = false;
15663 this.render(this.parent().getChildContainer(true), k);
15666 if(Roo.isIOS && this.useNativeIOS){
15667 this.initIOSView();
15675 if(Roo.isTouch && this.mobileTouchView){
15676 this.initTouchView();
15681 this.initTickableEvents();
15685 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15687 if(this.hiddenName){
15689 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15691 this.hiddenField.dom.value =
15692 this.hiddenValue !== undefined ? this.hiddenValue :
15693 this.value !== undefined ? this.value : '';
15695 // prevent input submission
15696 this.el.dom.removeAttribute('name');
15697 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15702 // this.el.dom.setAttribute('autocomplete', 'off');
15705 var cls = 'x-combo-list';
15707 //this.list = new Roo.Layer({
15708 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15714 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15715 _this.list.setWidth(lw);
15718 this.list.on('mouseover', this.onViewOver, this);
15719 this.list.on('mousemove', this.onViewMove, this);
15720 this.list.on('scroll', this.onViewScroll, this);
15723 this.list.swallowEvent('mousewheel');
15724 this.assetHeight = 0;
15727 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15728 this.assetHeight += this.header.getHeight();
15731 this.innerList = this.list.createChild({cls:cls+'-inner'});
15732 this.innerList.on('mouseover', this.onViewOver, this);
15733 this.innerList.on('mousemove', this.onViewMove, this);
15734 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15736 if(this.allowBlank && !this.pageSize && !this.disableClear){
15737 this.footer = this.list.createChild({cls:cls+'-ft'});
15738 this.pageTb = new Roo.Toolbar(this.footer);
15742 this.footer = this.list.createChild({cls:cls+'-ft'});
15743 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15744 {pageSize: this.pageSize});
15748 if (this.pageTb && this.allowBlank && !this.disableClear) {
15750 this.pageTb.add(new Roo.Toolbar.Fill(), {
15751 cls: 'x-btn-icon x-btn-clear',
15753 handler: function()
15756 _this.clearValue();
15757 _this.onSelect(false, -1);
15762 this.assetHeight += this.footer.getHeight();
15767 this.tpl = Roo.bootstrap.version == 4 ?
15768 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15769 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15772 this.view = new Roo.View(this.list, this.tpl, {
15773 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15775 //this.view.wrapEl.setDisplayed(false);
15776 this.view.on('click', this.onViewClick, this);
15779 this.store.on('beforeload', this.onBeforeLoad, this);
15780 this.store.on('load', this.onLoad, this);
15781 this.store.on('loadexception', this.onLoadException, this);
15783 if(this.resizable){
15784 this.resizer = new Roo.Resizable(this.list, {
15785 pinned:true, handles:'se'
15787 this.resizer.on('resize', function(r, w, h){
15788 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15789 this.listWidth = w;
15790 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15791 this.restrictHeight();
15793 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15796 if(!this.editable){
15797 this.editable = true;
15798 this.setEditable(false);
15803 if (typeof(this.events.add.listeners) != 'undefined') {
15805 this.addicon = this.wrap.createChild(
15806 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15808 this.addicon.on('click', function(e) {
15809 this.fireEvent('add', this);
15812 if (typeof(this.events.edit.listeners) != 'undefined') {
15814 this.editicon = this.wrap.createChild(
15815 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15816 if (this.addicon) {
15817 this.editicon.setStyle('margin-left', '40px');
15819 this.editicon.on('click', function(e) {
15821 // we fire even if inothing is selected..
15822 this.fireEvent('edit', this, this.lastData );
15828 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15829 "up" : function(e){
15830 this.inKeyMode = true;
15834 "down" : function(e){
15835 if(!this.isExpanded()){
15836 this.onTriggerClick();
15838 this.inKeyMode = true;
15843 "enter" : function(e){
15844 // this.onViewClick();
15848 if(this.fireEvent("specialkey", this, e)){
15849 this.onViewClick(false);
15855 "esc" : function(e){
15859 "tab" : function(e){
15862 if(this.fireEvent("specialkey", this, e)){
15863 this.onViewClick(false);
15871 doRelay : function(foo, bar, hname){
15872 if(hname == 'down' || this.scope.isExpanded()){
15873 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15882 this.queryDelay = Math.max(this.queryDelay || 10,
15883 this.mode == 'local' ? 10 : 250);
15886 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15888 if(this.typeAhead){
15889 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15891 if(this.editable !== false){
15892 this.inputEl().on("keyup", this.onKeyUp, this);
15894 if(this.forceSelection){
15895 this.inputEl().on('blur', this.doForce, this);
15899 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15900 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15904 initTickableEvents: function()
15908 if(this.hiddenName){
15910 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15912 this.hiddenField.dom.value =
15913 this.hiddenValue !== undefined ? this.hiddenValue :
15914 this.value !== undefined ? this.value : '';
15916 // prevent input submission
15917 this.el.dom.removeAttribute('name');
15918 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15923 // this.list = this.el.select('ul.dropdown-menu',true).first();
15925 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15926 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15927 if(this.triggerList){
15928 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15931 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15932 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15934 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15935 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15937 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15938 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15940 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15941 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15942 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15945 this.cancelBtn.hide();
15950 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15951 _this.list.setWidth(lw);
15954 this.list.on('mouseover', this.onViewOver, this);
15955 this.list.on('mousemove', this.onViewMove, this);
15957 this.list.on('scroll', this.onViewScroll, this);
15960 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15961 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15964 this.view = new Roo.View(this.list, this.tpl, {
15969 selectedClass: this.selectedClass
15972 //this.view.wrapEl.setDisplayed(false);
15973 this.view.on('click', this.onViewClick, this);
15977 this.store.on('beforeload', this.onBeforeLoad, this);
15978 this.store.on('load', this.onLoad, this);
15979 this.store.on('loadexception', this.onLoadException, this);
15982 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15983 "up" : function(e){
15984 this.inKeyMode = true;
15988 "down" : function(e){
15989 this.inKeyMode = true;
15993 "enter" : function(e){
15994 if(this.fireEvent("specialkey", this, e)){
15995 this.onViewClick(false);
16001 "esc" : function(e){
16002 this.onTickableFooterButtonClick(e, false, false);
16005 "tab" : function(e){
16006 this.fireEvent("specialkey", this, e);
16008 this.onTickableFooterButtonClick(e, false, false);
16015 doRelay : function(e, fn, key){
16016 if(this.scope.isExpanded()){
16017 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16026 this.queryDelay = Math.max(this.queryDelay || 10,
16027 this.mode == 'local' ? 10 : 250);
16030 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16032 if(this.typeAhead){
16033 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16036 if(this.editable !== false){
16037 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16040 this.indicator = this.indicatorEl();
16042 if(this.indicator){
16043 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16044 this.indicator.hide();
16049 onDestroy : function(){
16051 this.view.setStore(null);
16052 this.view.el.removeAllListeners();
16053 this.view.el.remove();
16054 this.view.purgeListeners();
16057 this.list.dom.innerHTML = '';
16061 this.store.un('beforeload', this.onBeforeLoad, this);
16062 this.store.un('load', this.onLoad, this);
16063 this.store.un('loadexception', this.onLoadException, this);
16065 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16069 fireKey : function(e){
16070 if(e.isNavKeyPress() && !this.list.isVisible()){
16071 this.fireEvent("specialkey", this, e);
16076 onResize: function(w, h)
16080 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16082 // if(typeof w != 'number'){
16083 // // we do not handle it!?!?
16086 // var tw = this.trigger.getWidth();
16087 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16088 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16090 // this.inputEl().setWidth( this.adjustWidth('input', x));
16092 // //this.trigger.setStyle('left', x+'px');
16094 // if(this.list && this.listWidth === undefined){
16095 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16096 // this.list.setWidth(lw);
16097 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16105 * Allow or prevent the user from directly editing the field text. If false is passed,
16106 * the user will only be able to select from the items defined in the dropdown list. This method
16107 * is the runtime equivalent of setting the 'editable' config option at config time.
16108 * @param {Boolean} value True to allow the user to directly edit the field text
16110 setEditable : function(value){
16111 if(value == this.editable){
16114 this.editable = value;
16116 this.inputEl().dom.setAttribute('readOnly', true);
16117 this.inputEl().on('mousedown', this.onTriggerClick, this);
16118 this.inputEl().addClass('x-combo-noedit');
16120 this.inputEl().dom.setAttribute('readOnly', false);
16121 this.inputEl().un('mousedown', this.onTriggerClick, this);
16122 this.inputEl().removeClass('x-combo-noedit');
16128 onBeforeLoad : function(combo,opts){
16129 if(!this.hasFocus){
16133 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16135 this.restrictHeight();
16136 this.selectedIndex = -1;
16140 onLoad : function(){
16142 this.hasQuery = false;
16144 if(!this.hasFocus){
16148 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16149 this.loading.hide();
16152 if(this.store.getCount() > 0){
16155 this.restrictHeight();
16156 if(this.lastQuery == this.allQuery){
16157 if(this.editable && !this.tickable){
16158 this.inputEl().dom.select();
16162 !this.selectByValue(this.value, true) &&
16165 !this.store.lastOptions ||
16166 typeof(this.store.lastOptions.add) == 'undefined' ||
16167 this.store.lastOptions.add != true
16170 this.select(0, true);
16173 if(this.autoFocus){
16176 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16177 this.taTask.delay(this.typeAheadDelay);
16181 this.onEmptyResults();
16187 onLoadException : function()
16189 this.hasQuery = false;
16191 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16192 this.loading.hide();
16195 if(this.tickable && this.editable){
16200 // only causes errors at present
16201 //Roo.log(this.store.reader.jsonData);
16202 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16204 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16210 onTypeAhead : function(){
16211 if(this.store.getCount() > 0){
16212 var r = this.store.getAt(0);
16213 var newValue = r.data[this.displayField];
16214 var len = newValue.length;
16215 var selStart = this.getRawValue().length;
16217 if(selStart != len){
16218 this.setRawValue(newValue);
16219 this.selectText(selStart, newValue.length);
16225 onSelect : function(record, index){
16227 if(this.fireEvent('beforeselect', this, record, index) !== false){
16229 this.setFromData(index > -1 ? record.data : false);
16232 this.fireEvent('select', this, record, index);
16237 * Returns the currently selected field value or empty string if no value is set.
16238 * @return {String} value The selected value
16240 getValue : function()
16242 if(Roo.isIOS && this.useNativeIOS){
16243 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16247 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16250 if(this.valueField){
16251 return typeof this.value != 'undefined' ? this.value : '';
16253 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16257 getRawValue : function()
16259 if(Roo.isIOS && this.useNativeIOS){
16260 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16263 var v = this.inputEl().getValue();
16269 * Clears any text/value currently set in the field
16271 clearValue : function(){
16273 if(this.hiddenField){
16274 this.hiddenField.dom.value = '';
16277 this.setRawValue('');
16278 this.lastSelectionText = '';
16279 this.lastData = false;
16281 var close = this.closeTriggerEl();
16292 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16293 * will be displayed in the field. If the value does not match the data value of an existing item,
16294 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16295 * Otherwise the field will be blank (although the value will still be set).
16296 * @param {String} value The value to match
16298 setValue : function(v)
16300 if(Roo.isIOS && this.useNativeIOS){
16301 this.setIOSValue(v);
16311 if(this.valueField){
16312 var r = this.findRecord(this.valueField, v);
16314 text = r.data[this.displayField];
16315 }else if(this.valueNotFoundText !== undefined){
16316 text = this.valueNotFoundText;
16319 this.lastSelectionText = text;
16320 if(this.hiddenField){
16321 this.hiddenField.dom.value = v;
16323 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16326 var close = this.closeTriggerEl();
16329 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16335 * @property {Object} the last set data for the element
16340 * Sets the value of the field based on a object which is related to the record format for the store.
16341 * @param {Object} value the value to set as. or false on reset?
16343 setFromData : function(o){
16350 var dv = ''; // display value
16351 var vv = ''; // value value..
16353 if (this.displayField) {
16354 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16356 // this is an error condition!!!
16357 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16360 if(this.valueField){
16361 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16364 var close = this.closeTriggerEl();
16367 if(dv.length || vv * 1 > 0){
16369 this.blockFocus=true;
16375 if(this.hiddenField){
16376 this.hiddenField.dom.value = vv;
16378 this.lastSelectionText = dv;
16379 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16383 // no hidden field.. - we store the value in 'value', but still display
16384 // display field!!!!
16385 this.lastSelectionText = dv;
16386 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16393 reset : function(){
16394 // overridden so that last data is reset..
16401 this.setValue(this.originalValue);
16402 //this.clearInvalid();
16403 this.lastData = false;
16405 this.view.clearSelections();
16411 findRecord : function(prop, value){
16413 if(this.store.getCount() > 0){
16414 this.store.each(function(r){
16415 if(r.data[prop] == value){
16425 getName: function()
16427 // returns hidden if it's set..
16428 if (!this.rendered) {return ''};
16429 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16433 onViewMove : function(e, t){
16434 this.inKeyMode = false;
16438 onViewOver : function(e, t){
16439 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16442 var item = this.view.findItemFromChild(t);
16445 var index = this.view.indexOf(item);
16446 this.select(index, false);
16451 onViewClick : function(view, doFocus, el, e)
16453 var index = this.view.getSelectedIndexes()[0];
16455 var r = this.store.getAt(index);
16459 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16466 Roo.each(this.tickItems, function(v,k){
16468 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16470 _this.tickItems.splice(k, 1);
16472 if(typeof(e) == 'undefined' && view == false){
16473 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16485 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16486 this.tickItems.push(r.data);
16489 if(typeof(e) == 'undefined' && view == false){
16490 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16497 this.onSelect(r, index);
16499 if(doFocus !== false && !this.blockFocus){
16500 this.inputEl().focus();
16505 restrictHeight : function(){
16506 //this.innerList.dom.style.height = '';
16507 //var inner = this.innerList.dom;
16508 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16509 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16510 //this.list.beginUpdate();
16511 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16512 this.list.alignTo(this.inputEl(), this.listAlign);
16513 this.list.alignTo(this.inputEl(), this.listAlign);
16514 //this.list.endUpdate();
16518 onEmptyResults : function(){
16520 if(this.tickable && this.editable){
16521 this.hasFocus = false;
16522 this.restrictHeight();
16530 * Returns true if the dropdown list is expanded, else false.
16532 isExpanded : function(){
16533 return this.list.isVisible();
16537 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16538 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16539 * @param {String} value The data value of the item to select
16540 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16541 * selected item if it is not currently in view (defaults to true)
16542 * @return {Boolean} True if the value matched an item in the list, else false
16544 selectByValue : function(v, scrollIntoView){
16545 if(v !== undefined && v !== null){
16546 var r = this.findRecord(this.valueField || this.displayField, v);
16548 this.select(this.store.indexOf(r), scrollIntoView);
16556 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16557 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16558 * @param {Number} index The zero-based index of the list item to select
16559 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16560 * selected item if it is not currently in view (defaults to true)
16562 select : function(index, scrollIntoView){
16563 this.selectedIndex = index;
16564 this.view.select(index);
16565 if(scrollIntoView !== false){
16566 var el = this.view.getNode(index);
16568 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16571 this.list.scrollChildIntoView(el, false);
16577 selectNext : function(){
16578 var ct = this.store.getCount();
16580 if(this.selectedIndex == -1){
16582 }else if(this.selectedIndex < ct-1){
16583 this.select(this.selectedIndex+1);
16589 selectPrev : function(){
16590 var ct = this.store.getCount();
16592 if(this.selectedIndex == -1){
16594 }else if(this.selectedIndex != 0){
16595 this.select(this.selectedIndex-1);
16601 onKeyUp : function(e){
16602 if(this.editable !== false && !e.isSpecialKey()){
16603 this.lastKey = e.getKey();
16604 this.dqTask.delay(this.queryDelay);
16609 validateBlur : function(){
16610 return !this.list || !this.list.isVisible();
16614 initQuery : function(){
16616 var v = this.getRawValue();
16618 if(this.tickable && this.editable){
16619 v = this.tickableInputEl().getValue();
16626 doForce : function(){
16627 if(this.inputEl().dom.value.length > 0){
16628 this.inputEl().dom.value =
16629 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16635 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16636 * query allowing the query action to be canceled if needed.
16637 * @param {String} query The SQL query to execute
16638 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16639 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16640 * saved in the current store (defaults to false)
16642 doQuery : function(q, forceAll){
16644 if(q === undefined || q === null){
16649 forceAll: forceAll,
16653 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16658 forceAll = qe.forceAll;
16659 if(forceAll === true || (q.length >= this.minChars)){
16661 this.hasQuery = true;
16663 if(this.lastQuery != q || this.alwaysQuery){
16664 this.lastQuery = q;
16665 if(this.mode == 'local'){
16666 this.selectedIndex = -1;
16668 this.store.clearFilter();
16671 if(this.specialFilter){
16672 this.fireEvent('specialfilter', this);
16677 this.store.filter(this.displayField, q);
16680 this.store.fireEvent("datachanged", this.store);
16687 this.store.baseParams[this.queryParam] = q;
16689 var options = {params : this.getParams(q)};
16692 options.add = true;
16693 options.params.start = this.page * this.pageSize;
16696 this.store.load(options);
16699 * this code will make the page width larger, at the beginning, the list not align correctly,
16700 * we should expand the list on onLoad
16701 * so command out it
16706 this.selectedIndex = -1;
16711 this.loadNext = false;
16715 getParams : function(q){
16717 //p[this.queryParam] = q;
16721 p.limit = this.pageSize;
16727 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16729 collapse : function(){
16730 if(!this.isExpanded()){
16736 this.hasFocus = false;
16740 this.cancelBtn.hide();
16741 this.trigger.show();
16744 this.tickableInputEl().dom.value = '';
16745 this.tickableInputEl().blur();
16750 Roo.get(document).un('mousedown', this.collapseIf, this);
16751 Roo.get(document).un('mousewheel', this.collapseIf, this);
16752 if (!this.editable) {
16753 Roo.get(document).un('keydown', this.listKeyPress, this);
16755 this.fireEvent('collapse', this);
16761 collapseIf : function(e){
16762 var in_combo = e.within(this.el);
16763 var in_list = e.within(this.list);
16764 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16766 if (in_combo || in_list || is_list) {
16767 //e.stopPropagation();
16772 this.onTickableFooterButtonClick(e, false, false);
16780 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16782 expand : function(){
16784 if(this.isExpanded() || !this.hasFocus){
16788 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16789 this.list.setWidth(lw);
16795 this.restrictHeight();
16799 this.tickItems = Roo.apply([], this.item);
16802 this.cancelBtn.show();
16803 this.trigger.hide();
16806 this.tickableInputEl().focus();
16811 Roo.get(document).on('mousedown', this.collapseIf, this);
16812 Roo.get(document).on('mousewheel', this.collapseIf, this);
16813 if (!this.editable) {
16814 Roo.get(document).on('keydown', this.listKeyPress, this);
16817 this.fireEvent('expand', this);
16821 // Implements the default empty TriggerField.onTriggerClick function
16822 onTriggerClick : function(e)
16824 Roo.log('trigger click');
16826 if(this.disabled || !this.triggerList){
16831 this.loadNext = false;
16833 if(this.isExpanded()){
16835 if (!this.blockFocus) {
16836 this.inputEl().focus();
16840 this.hasFocus = true;
16841 if(this.triggerAction == 'all') {
16842 this.doQuery(this.allQuery, true);
16844 this.doQuery(this.getRawValue());
16846 if (!this.blockFocus) {
16847 this.inputEl().focus();
16852 onTickableTriggerClick : function(e)
16859 this.loadNext = false;
16860 this.hasFocus = true;
16862 if(this.triggerAction == 'all') {
16863 this.doQuery(this.allQuery, true);
16865 this.doQuery(this.getRawValue());
16869 onSearchFieldClick : function(e)
16871 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16872 this.onTickableFooterButtonClick(e, false, false);
16876 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16881 this.loadNext = false;
16882 this.hasFocus = true;
16884 if(this.triggerAction == 'all') {
16885 this.doQuery(this.allQuery, true);
16887 this.doQuery(this.getRawValue());
16891 listKeyPress : function(e)
16893 //Roo.log('listkeypress');
16894 // scroll to first matching element based on key pres..
16895 if (e.isSpecialKey()) {
16898 var k = String.fromCharCode(e.getKey()).toUpperCase();
16901 var csel = this.view.getSelectedNodes();
16902 var cselitem = false;
16904 var ix = this.view.indexOf(csel[0]);
16905 cselitem = this.store.getAt(ix);
16906 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16912 this.store.each(function(v) {
16914 // start at existing selection.
16915 if (cselitem.id == v.id) {
16921 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16922 match = this.store.indexOf(v);
16928 if (match === false) {
16929 return true; // no more action?
16932 this.view.select(match);
16933 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16934 sn.scrollIntoView(sn.dom.parentNode, false);
16937 onViewScroll : function(e, t){
16939 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){
16943 this.hasQuery = true;
16945 this.loading = this.list.select('.loading', true).first();
16947 if(this.loading === null){
16948 this.list.createChild({
16950 cls: 'loading roo-select2-more-results roo-select2-active',
16951 html: 'Loading more results...'
16954 this.loading = this.list.select('.loading', true).first();
16956 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16958 this.loading.hide();
16961 this.loading.show();
16966 this.loadNext = true;
16968 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16973 addItem : function(o)
16975 var dv = ''; // display value
16977 if (this.displayField) {
16978 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16980 // this is an error condition!!!
16981 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16988 var choice = this.choices.createChild({
16990 cls: 'roo-select2-search-choice',
16999 cls: 'roo-select2-search-choice-close fa fa-times',
17004 }, this.searchField);
17006 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17008 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17016 this.inputEl().dom.value = '';
17021 onRemoveItem : function(e, _self, o)
17023 e.preventDefault();
17025 this.lastItem = Roo.apply([], this.item);
17027 var index = this.item.indexOf(o.data) * 1;
17030 Roo.log('not this item?!');
17034 this.item.splice(index, 1);
17039 this.fireEvent('remove', this, e);
17045 syncValue : function()
17047 if(!this.item.length){
17054 Roo.each(this.item, function(i){
17055 if(_this.valueField){
17056 value.push(i[_this.valueField]);
17063 this.value = value.join(',');
17065 if(this.hiddenField){
17066 this.hiddenField.dom.value = this.value;
17069 this.store.fireEvent("datachanged", this.store);
17074 clearItem : function()
17076 if(!this.multiple){
17082 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17090 if(this.tickable && !Roo.isTouch){
17091 this.view.refresh();
17095 inputEl: function ()
17097 if(Roo.isIOS && this.useNativeIOS){
17098 return this.el.select('select.roo-ios-select', true).first();
17101 if(Roo.isTouch && this.mobileTouchView){
17102 return this.el.select('input.form-control',true).first();
17106 return this.searchField;
17109 return this.el.select('input.form-control',true).first();
17112 onTickableFooterButtonClick : function(e, btn, el)
17114 e.preventDefault();
17116 this.lastItem = Roo.apply([], this.item);
17118 if(btn && btn.name == 'cancel'){
17119 this.tickItems = Roo.apply([], this.item);
17128 Roo.each(this.tickItems, function(o){
17136 validate : function()
17138 if(this.getVisibilityEl().hasClass('hidden')){
17142 var v = this.getRawValue();
17145 v = this.getValue();
17148 if(this.disabled || this.allowBlank || v.length){
17153 this.markInvalid();
17157 tickableInputEl : function()
17159 if(!this.tickable || !this.editable){
17160 return this.inputEl();
17163 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17167 getAutoCreateTouchView : function()
17172 cls: 'form-group' //input-group
17178 type : this.inputType,
17179 cls : 'form-control x-combo-noedit',
17180 autocomplete: 'new-password',
17181 placeholder : this.placeholder || '',
17186 input.name = this.name;
17190 input.cls += ' input-' + this.size;
17193 if (this.disabled) {
17194 input.disabled = true;
17198 cls : 'roo-combobox-wrap',
17205 inputblock.cls += ' input-group';
17207 inputblock.cn.unshift({
17209 cls : 'input-group-addon input-group-prepend input-group-text',
17214 if(this.removable && !this.multiple){
17215 inputblock.cls += ' roo-removable';
17217 inputblock.cn.push({
17220 cls : 'roo-combo-removable-btn close'
17224 if(this.hasFeedback && !this.allowBlank){
17226 inputblock.cls += ' has-feedback';
17228 inputblock.cn.push({
17230 cls: 'glyphicon form-control-feedback'
17237 inputblock.cls += (this.before) ? '' : ' input-group';
17239 inputblock.cn.push({
17241 cls : 'input-group-addon input-group-append input-group-text',
17247 var ibwrap = inputblock;
17252 cls: 'roo-select2-choices',
17256 cls: 'roo-select2-search-field',
17269 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17274 cls: 'form-hidden-field'
17280 if(!this.multiple && this.showToggleBtn){
17286 if (this.caret != false) {
17289 cls: 'fa fa-' + this.caret
17296 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17298 Roo.bootstrap.version == 3 ? caret : '',
17301 cls: 'combobox-clear',
17315 combobox.cls += ' roo-select2-container-multi';
17318 var align = this.labelAlign || this.parentLabelAlign();
17320 if (align ==='left' && this.fieldLabel.length) {
17325 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17326 tooltip : 'This field is required'
17330 cls : 'control-label col-form-label',
17331 html : this.fieldLabel
17335 cls : 'roo-combobox-wrap ',
17342 var labelCfg = cfg.cn[1];
17343 var contentCfg = cfg.cn[2];
17346 if(this.indicatorpos == 'right'){
17351 cls : 'control-label col-form-label',
17355 html : this.fieldLabel
17359 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17360 tooltip : 'This field is required'
17365 cls : "roo-combobox-wrap ",
17373 labelCfg = cfg.cn[0];
17374 contentCfg = cfg.cn[1];
17379 if(this.labelWidth > 12){
17380 labelCfg.style = "width: " + this.labelWidth + 'px';
17383 if(this.labelWidth < 13 && this.labelmd == 0){
17384 this.labelmd = this.labelWidth;
17387 if(this.labellg > 0){
17388 labelCfg.cls += ' col-lg-' + this.labellg;
17389 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17392 if(this.labelmd > 0){
17393 labelCfg.cls += ' col-md-' + this.labelmd;
17394 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17397 if(this.labelsm > 0){
17398 labelCfg.cls += ' col-sm-' + this.labelsm;
17399 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17402 if(this.labelxs > 0){
17403 labelCfg.cls += ' col-xs-' + this.labelxs;
17404 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17408 } else if ( this.fieldLabel.length) {
17412 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17413 tooltip : 'This field is required'
17417 cls : 'control-label',
17418 html : this.fieldLabel
17429 if(this.indicatorpos == 'right'){
17433 cls : 'control-label',
17434 html : this.fieldLabel,
17438 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17439 tooltip : 'This field is required'
17456 var settings = this;
17458 ['xs','sm','md','lg'].map(function(size){
17459 if (settings[size]) {
17460 cfg.cls += ' col-' + size + '-' + settings[size];
17467 initTouchView : function()
17469 this.renderTouchView();
17471 this.touchViewEl.on('scroll', function(){
17472 this.el.dom.scrollTop = 0;
17475 this.originalValue = this.getValue();
17477 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17479 this.inputEl().on("click", this.showTouchView, this);
17480 if (this.triggerEl) {
17481 this.triggerEl.on("click", this.showTouchView, this);
17485 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17486 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17488 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17490 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17491 this.store.on('load', this.onTouchViewLoad, this);
17492 this.store.on('loadexception', this.onTouchViewLoadException, this);
17494 if(this.hiddenName){
17496 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17498 this.hiddenField.dom.value =
17499 this.hiddenValue !== undefined ? this.hiddenValue :
17500 this.value !== undefined ? this.value : '';
17502 this.el.dom.removeAttribute('name');
17503 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17507 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17508 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17511 if(this.removable && !this.multiple){
17512 var close = this.closeTriggerEl();
17514 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17515 close.on('click', this.removeBtnClick, this, close);
17519 * fix the bug in Safari iOS8
17521 this.inputEl().on("focus", function(e){
17522 document.activeElement.blur();
17525 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17532 renderTouchView : function()
17534 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17535 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17537 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17538 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17540 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17541 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17542 this.touchViewBodyEl.setStyle('overflow', 'auto');
17544 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17545 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17547 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17548 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17552 showTouchView : function()
17558 this.touchViewHeaderEl.hide();
17560 if(this.modalTitle.length){
17561 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17562 this.touchViewHeaderEl.show();
17565 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17566 this.touchViewEl.show();
17568 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17570 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17571 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17573 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17575 if(this.modalTitle.length){
17576 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17579 this.touchViewBodyEl.setHeight(bodyHeight);
17583 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17585 this.touchViewEl.addClass(['in','show']);
17588 if(this._touchViewMask){
17589 Roo.get(document.body).addClass("x-body-masked");
17590 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17591 this._touchViewMask.setStyle('z-index', 10000);
17592 this._touchViewMask.addClass('show');
17595 this.doTouchViewQuery();
17599 hideTouchView : function()
17601 this.touchViewEl.removeClass(['in','show']);
17605 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17607 this.touchViewEl.setStyle('display', 'none');
17610 if(this._touchViewMask){
17611 this._touchViewMask.removeClass('show');
17612 Roo.get(document.body).removeClass("x-body-masked");
17616 setTouchViewValue : function()
17623 Roo.each(this.tickItems, function(o){
17628 this.hideTouchView();
17631 doTouchViewQuery : function()
17640 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17644 if(!this.alwaysQuery || this.mode == 'local'){
17645 this.onTouchViewLoad();
17652 onTouchViewBeforeLoad : function(combo,opts)
17658 onTouchViewLoad : function()
17660 if(this.store.getCount() < 1){
17661 this.onTouchViewEmptyResults();
17665 this.clearTouchView();
17667 var rawValue = this.getRawValue();
17669 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17671 this.tickItems = [];
17673 this.store.data.each(function(d, rowIndex){
17674 var row = this.touchViewListGroup.createChild(template);
17676 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17677 row.addClass(d.data.cls);
17680 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17683 html : d.data[this.displayField]
17686 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17687 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17690 row.removeClass('selected');
17691 if(!this.multiple && this.valueField &&
17692 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17695 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17696 row.addClass('selected');
17699 if(this.multiple && this.valueField &&
17700 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17704 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17705 this.tickItems.push(d.data);
17708 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17712 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17714 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17716 if(this.modalTitle.length){
17717 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17720 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17722 if(this.mobile_restrict_height && listHeight < bodyHeight){
17723 this.touchViewBodyEl.setHeight(listHeight);
17728 if(firstChecked && listHeight > bodyHeight){
17729 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17734 onTouchViewLoadException : function()
17736 this.hideTouchView();
17739 onTouchViewEmptyResults : function()
17741 this.clearTouchView();
17743 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17745 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17749 clearTouchView : function()
17751 this.touchViewListGroup.dom.innerHTML = '';
17754 onTouchViewClick : function(e, el, o)
17756 e.preventDefault();
17759 var rowIndex = o.rowIndex;
17761 var r = this.store.getAt(rowIndex);
17763 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17765 if(!this.multiple){
17766 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17767 c.dom.removeAttribute('checked');
17770 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17772 this.setFromData(r.data);
17774 var close = this.closeTriggerEl();
17780 this.hideTouchView();
17782 this.fireEvent('select', this, r, rowIndex);
17787 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17788 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17789 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17793 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17794 this.addItem(r.data);
17795 this.tickItems.push(r.data);
17799 getAutoCreateNativeIOS : function()
17802 cls: 'form-group' //input-group,
17807 cls : 'roo-ios-select'
17811 combobox.name = this.name;
17814 if (this.disabled) {
17815 combobox.disabled = true;
17818 var settings = this;
17820 ['xs','sm','md','lg'].map(function(size){
17821 if (settings[size]) {
17822 cfg.cls += ' col-' + size + '-' + settings[size];
17832 initIOSView : function()
17834 this.store.on('load', this.onIOSViewLoad, this);
17839 onIOSViewLoad : function()
17841 if(this.store.getCount() < 1){
17845 this.clearIOSView();
17847 if(this.allowBlank) {
17849 var default_text = '-- SELECT --';
17851 if(this.placeholder.length){
17852 default_text = this.placeholder;
17855 if(this.emptyTitle.length){
17856 default_text += ' - ' + this.emptyTitle + ' -';
17859 var opt = this.inputEl().createChild({
17862 html : default_text
17866 o[this.valueField] = 0;
17867 o[this.displayField] = default_text;
17869 this.ios_options.push({
17876 this.store.data.each(function(d, rowIndex){
17880 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17881 html = d.data[this.displayField];
17886 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17887 value = d.data[this.valueField];
17896 if(this.value == d.data[this.valueField]){
17897 option['selected'] = true;
17900 var opt = this.inputEl().createChild(option);
17902 this.ios_options.push({
17909 this.inputEl().on('change', function(){
17910 this.fireEvent('select', this);
17915 clearIOSView: function()
17917 this.inputEl().dom.innerHTML = '';
17919 this.ios_options = [];
17922 setIOSValue: function(v)
17926 if(!this.ios_options){
17930 Roo.each(this.ios_options, function(opts){
17932 opts.el.dom.removeAttribute('selected');
17934 if(opts.data[this.valueField] != v){
17938 opts.el.dom.setAttribute('selected', true);
17944 * @cfg {Boolean} grow
17948 * @cfg {Number} growMin
17952 * @cfg {Number} growMax
17961 Roo.apply(Roo.bootstrap.ComboBox, {
17965 cls: 'modal-header',
17987 cls: 'list-group-item',
17991 cls: 'roo-combobox-list-group-item-value'
17995 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18009 listItemCheckbox : {
18011 cls: 'list-group-item',
18015 cls: 'roo-combobox-list-group-item-value'
18019 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18035 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18040 cls: 'modal-footer',
18048 cls: 'col-xs-6 text-left',
18051 cls: 'btn btn-danger roo-touch-view-cancel',
18057 cls: 'col-xs-6 text-right',
18060 cls: 'btn btn-success roo-touch-view-ok',
18071 Roo.apply(Roo.bootstrap.ComboBox, {
18073 touchViewTemplate : {
18075 cls: 'modal fade roo-combobox-touch-view',
18079 cls: 'modal-dialog',
18080 style : 'position:fixed', // we have to fix position....
18084 cls: 'modal-content',
18086 Roo.bootstrap.ComboBox.header,
18087 Roo.bootstrap.ComboBox.body,
18088 Roo.bootstrap.ComboBox.footer
18097 * Ext JS Library 1.1.1
18098 * Copyright(c) 2006-2007, Ext JS, LLC.
18100 * Originally Released Under LGPL - original licence link has changed is not relivant.
18103 * <script type="text/javascript">
18108 * @extends Roo.util.Observable
18109 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18110 * This class also supports single and multi selection modes. <br>
18111 * Create a data model bound view:
18113 var store = new Roo.data.Store(...);
18115 var view = new Roo.View({
18117 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18119 singleSelect: true,
18120 selectedClass: "ydataview-selected",
18124 // listen for node click?
18125 view.on("click", function(vw, index, node, e){
18126 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18130 dataModel.load("foobar.xml");
18132 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18134 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18135 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18137 * Note: old style constructor is still suported (container, template, config)
18140 * Create a new View
18141 * @param {Object} config The config object
18144 Roo.View = function(config, depreciated_tpl, depreciated_config){
18146 this.parent = false;
18148 if (typeof(depreciated_tpl) == 'undefined') {
18149 // new way.. - universal constructor.
18150 Roo.apply(this, config);
18151 this.el = Roo.get(this.el);
18154 this.el = Roo.get(config);
18155 this.tpl = depreciated_tpl;
18156 Roo.apply(this, depreciated_config);
18158 this.wrapEl = this.el.wrap().wrap();
18159 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18162 if(typeof(this.tpl) == "string"){
18163 this.tpl = new Roo.Template(this.tpl);
18165 // support xtype ctors..
18166 this.tpl = new Roo.factory(this.tpl, Roo);
18170 this.tpl.compile();
18175 * @event beforeclick
18176 * Fires before a click is processed. Returns false to cancel the default action.
18177 * @param {Roo.View} this
18178 * @param {Number} index The index of the target node
18179 * @param {HTMLElement} node The target node
18180 * @param {Roo.EventObject} e The raw event object
18182 "beforeclick" : true,
18185 * Fires when a template node is clicked.
18186 * @param {Roo.View} this
18187 * @param {Number} index The index of the target node
18188 * @param {HTMLElement} node The target node
18189 * @param {Roo.EventObject} e The raw event object
18194 * Fires when a template node is double clicked.
18195 * @param {Roo.View} this
18196 * @param {Number} index The index of the target node
18197 * @param {HTMLElement} node The target node
18198 * @param {Roo.EventObject} e The raw event object
18202 * @event contextmenu
18203 * Fires when a template node is right clicked.
18204 * @param {Roo.View} this
18205 * @param {Number} index The index of the target node
18206 * @param {HTMLElement} node The target node
18207 * @param {Roo.EventObject} e The raw event object
18209 "contextmenu" : true,
18211 * @event selectionchange
18212 * Fires when the selected nodes change.
18213 * @param {Roo.View} this
18214 * @param {Array} selections Array of the selected nodes
18216 "selectionchange" : true,
18219 * @event beforeselect
18220 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18221 * @param {Roo.View} this
18222 * @param {HTMLElement} node The node to be selected
18223 * @param {Array} selections Array of currently selected nodes
18225 "beforeselect" : true,
18227 * @event preparedata
18228 * Fires on every row to render, to allow you to change the data.
18229 * @param {Roo.View} this
18230 * @param {Object} data to be rendered (change this)
18232 "preparedata" : true
18240 "click": this.onClick,
18241 "dblclick": this.onDblClick,
18242 "contextmenu": this.onContextMenu,
18246 this.selections = [];
18248 this.cmp = new Roo.CompositeElementLite([]);
18250 this.store = Roo.factory(this.store, Roo.data);
18251 this.setStore(this.store, true);
18254 if ( this.footer && this.footer.xtype) {
18256 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18258 this.footer.dataSource = this.store;
18259 this.footer.container = fctr;
18260 this.footer = Roo.factory(this.footer, Roo);
18261 fctr.insertFirst(this.el);
18263 // this is a bit insane - as the paging toolbar seems to detach the el..
18264 // dom.parentNode.parentNode.parentNode
18265 // they get detached?
18269 Roo.View.superclass.constructor.call(this);
18274 Roo.extend(Roo.View, Roo.util.Observable, {
18277 * @cfg {Roo.data.Store} store Data store to load data from.
18282 * @cfg {String|Roo.Element} el The container element.
18287 * @cfg {String|Roo.Template} tpl The template used by this View
18291 * @cfg {String} dataName the named area of the template to use as the data area
18292 * Works with domtemplates roo-name="name"
18296 * @cfg {String} selectedClass The css class to add to selected nodes
18298 selectedClass : "x-view-selected",
18300 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18305 * @cfg {String} text to display on mask (default Loading)
18309 * @cfg {Boolean} multiSelect Allow multiple selection
18311 multiSelect : false,
18313 * @cfg {Boolean} singleSelect Allow single selection
18315 singleSelect: false,
18318 * @cfg {Boolean} toggleSelect - selecting
18320 toggleSelect : false,
18323 * @cfg {Boolean} tickable - selecting
18328 * Returns the element this view is bound to.
18329 * @return {Roo.Element}
18331 getEl : function(){
18332 return this.wrapEl;
18338 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18340 refresh : function(){
18341 //Roo.log('refresh');
18344 // if we are using something like 'domtemplate', then
18345 // the what gets used is:
18346 // t.applySubtemplate(NAME, data, wrapping data..)
18347 // the outer template then get' applied with
18348 // the store 'extra data'
18349 // and the body get's added to the
18350 // roo-name="data" node?
18351 // <span class='roo-tpl-{name}'></span> ?????
18355 this.clearSelections();
18356 this.el.update("");
18358 var records = this.store.getRange();
18359 if(records.length < 1) {
18361 // is this valid?? = should it render a template??
18363 this.el.update(this.emptyText);
18367 if (this.dataName) {
18368 this.el.update(t.apply(this.store.meta)); //????
18369 el = this.el.child('.roo-tpl-' + this.dataName);
18372 for(var i = 0, len = records.length; i < len; i++){
18373 var data = this.prepareData(records[i].data, i, records[i]);
18374 this.fireEvent("preparedata", this, data, i, records[i]);
18376 var d = Roo.apply({}, data);
18379 Roo.apply(d, {'roo-id' : Roo.id()});
18383 Roo.each(this.parent.item, function(item){
18384 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18387 Roo.apply(d, {'roo-data-checked' : 'checked'});
18391 html[html.length] = Roo.util.Format.trim(
18393 t.applySubtemplate(this.dataName, d, this.store.meta) :
18400 el.update(html.join(""));
18401 this.nodes = el.dom.childNodes;
18402 this.updateIndexes(0);
18407 * Function to override to reformat the data that is sent to
18408 * the template for each node.
18409 * DEPRICATED - use the preparedata event handler.
18410 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18411 * a JSON object for an UpdateManager bound view).
18413 prepareData : function(data, index, record)
18415 this.fireEvent("preparedata", this, data, index, record);
18419 onUpdate : function(ds, record){
18420 // Roo.log('on update');
18421 this.clearSelections();
18422 var index = this.store.indexOf(record);
18423 var n = this.nodes[index];
18424 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18425 n.parentNode.removeChild(n);
18426 this.updateIndexes(index, index);
18432 onAdd : function(ds, records, index)
18434 //Roo.log(['on Add', ds, records, index] );
18435 this.clearSelections();
18436 if(this.nodes.length == 0){
18440 var n = this.nodes[index];
18441 for(var i = 0, len = records.length; i < len; i++){
18442 var d = this.prepareData(records[i].data, i, records[i]);
18444 this.tpl.insertBefore(n, d);
18447 this.tpl.append(this.el, d);
18450 this.updateIndexes(index);
18453 onRemove : function(ds, record, index){
18454 // Roo.log('onRemove');
18455 this.clearSelections();
18456 var el = this.dataName ?
18457 this.el.child('.roo-tpl-' + this.dataName) :
18460 el.dom.removeChild(this.nodes[index]);
18461 this.updateIndexes(index);
18465 * Refresh an individual node.
18466 * @param {Number} index
18468 refreshNode : function(index){
18469 this.onUpdate(this.store, this.store.getAt(index));
18472 updateIndexes : function(startIndex, endIndex){
18473 var ns = this.nodes;
18474 startIndex = startIndex || 0;
18475 endIndex = endIndex || ns.length - 1;
18476 for(var i = startIndex; i <= endIndex; i++){
18477 ns[i].nodeIndex = i;
18482 * Changes the data store this view uses and refresh the view.
18483 * @param {Store} store
18485 setStore : function(store, initial){
18486 if(!initial && this.store){
18487 this.store.un("datachanged", this.refresh);
18488 this.store.un("add", this.onAdd);
18489 this.store.un("remove", this.onRemove);
18490 this.store.un("update", this.onUpdate);
18491 this.store.un("clear", this.refresh);
18492 this.store.un("beforeload", this.onBeforeLoad);
18493 this.store.un("load", this.onLoad);
18494 this.store.un("loadexception", this.onLoad);
18498 store.on("datachanged", this.refresh, this);
18499 store.on("add", this.onAdd, this);
18500 store.on("remove", this.onRemove, this);
18501 store.on("update", this.onUpdate, this);
18502 store.on("clear", this.refresh, this);
18503 store.on("beforeload", this.onBeforeLoad, this);
18504 store.on("load", this.onLoad, this);
18505 store.on("loadexception", this.onLoad, this);
18513 * onbeforeLoad - masks the loading area.
18516 onBeforeLoad : function(store,opts)
18518 //Roo.log('onBeforeLoad');
18520 this.el.update("");
18522 this.el.mask(this.mask ? this.mask : "Loading" );
18524 onLoad : function ()
18531 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18532 * @param {HTMLElement} node
18533 * @return {HTMLElement} The template node
18535 findItemFromChild : function(node){
18536 var el = this.dataName ?
18537 this.el.child('.roo-tpl-' + this.dataName,true) :
18540 if(!node || node.parentNode == el){
18543 var p = node.parentNode;
18544 while(p && p != el){
18545 if(p.parentNode == el){
18554 onClick : function(e){
18555 var item = this.findItemFromChild(e.getTarget());
18557 var index = this.indexOf(item);
18558 if(this.onItemClick(item, index, e) !== false){
18559 this.fireEvent("click", this, index, item, e);
18562 this.clearSelections();
18567 onContextMenu : function(e){
18568 var item = this.findItemFromChild(e.getTarget());
18570 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18575 onDblClick : function(e){
18576 var item = this.findItemFromChild(e.getTarget());
18578 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18582 onItemClick : function(item, index, e)
18584 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18587 if (this.toggleSelect) {
18588 var m = this.isSelected(item) ? 'unselect' : 'select';
18591 _t[m](item, true, false);
18594 if(this.multiSelect || this.singleSelect){
18595 if(this.multiSelect && e.shiftKey && this.lastSelection){
18596 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18598 this.select(item, this.multiSelect && e.ctrlKey);
18599 this.lastSelection = item;
18602 if(!this.tickable){
18603 e.preventDefault();
18611 * Get the number of selected nodes.
18614 getSelectionCount : function(){
18615 return this.selections.length;
18619 * Get the currently selected nodes.
18620 * @return {Array} An array of HTMLElements
18622 getSelectedNodes : function(){
18623 return this.selections;
18627 * Get the indexes of the selected nodes.
18630 getSelectedIndexes : function(){
18631 var indexes = [], s = this.selections;
18632 for(var i = 0, len = s.length; i < len; i++){
18633 indexes.push(s[i].nodeIndex);
18639 * Clear all selections
18640 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18642 clearSelections : function(suppressEvent){
18643 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18644 this.cmp.elements = this.selections;
18645 this.cmp.removeClass(this.selectedClass);
18646 this.selections = [];
18647 if(!suppressEvent){
18648 this.fireEvent("selectionchange", this, this.selections);
18654 * Returns true if the passed node is selected
18655 * @param {HTMLElement/Number} node The node or node index
18656 * @return {Boolean}
18658 isSelected : function(node){
18659 var s = this.selections;
18663 node = this.getNode(node);
18664 return s.indexOf(node) !== -1;
18669 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18670 * @param {Boolean} keepExisting (optional) true to keep existing selections
18671 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18673 select : function(nodeInfo, keepExisting, suppressEvent){
18674 if(nodeInfo instanceof Array){
18676 this.clearSelections(true);
18678 for(var i = 0, len = nodeInfo.length; i < len; i++){
18679 this.select(nodeInfo[i], true, true);
18683 var node = this.getNode(nodeInfo);
18684 if(!node || this.isSelected(node)){
18685 return; // already selected.
18688 this.clearSelections(true);
18691 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18692 Roo.fly(node).addClass(this.selectedClass);
18693 this.selections.push(node);
18694 if(!suppressEvent){
18695 this.fireEvent("selectionchange", this, this.selections);
18703 * @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
18704 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18705 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18707 unselect : function(nodeInfo, keepExisting, suppressEvent)
18709 if(nodeInfo instanceof Array){
18710 Roo.each(this.selections, function(s) {
18711 this.unselect(s, nodeInfo);
18715 var node = this.getNode(nodeInfo);
18716 if(!node || !this.isSelected(node)){
18717 //Roo.log("not selected");
18718 return; // not selected.
18722 Roo.each(this.selections, function(s) {
18724 Roo.fly(node).removeClass(this.selectedClass);
18731 this.selections= ns;
18732 this.fireEvent("selectionchange", this, this.selections);
18736 * Gets a template node.
18737 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18738 * @return {HTMLElement} The node or null if it wasn't found
18740 getNode : function(nodeInfo){
18741 if(typeof nodeInfo == "string"){
18742 return document.getElementById(nodeInfo);
18743 }else if(typeof nodeInfo == "number"){
18744 return this.nodes[nodeInfo];
18750 * Gets a range template nodes.
18751 * @param {Number} startIndex
18752 * @param {Number} endIndex
18753 * @return {Array} An array of nodes
18755 getNodes : function(start, end){
18756 var ns = this.nodes;
18757 start = start || 0;
18758 end = typeof end == "undefined" ? ns.length - 1 : end;
18761 for(var i = start; i <= end; i++){
18765 for(var i = start; i >= end; i--){
18773 * Finds the index of the passed node
18774 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18775 * @return {Number} The index of the node or -1
18777 indexOf : function(node){
18778 node = this.getNode(node);
18779 if(typeof node.nodeIndex == "number"){
18780 return node.nodeIndex;
18782 var ns = this.nodes;
18783 for(var i = 0, len = ns.length; i < len; i++){
18794 * based on jquery fullcalendar
18798 Roo.bootstrap = Roo.bootstrap || {};
18800 * @class Roo.bootstrap.Calendar
18801 * @extends Roo.bootstrap.Component
18802 * Bootstrap Calendar class
18803 * @cfg {Boolean} loadMask (true|false) default false
18804 * @cfg {Object} header generate the user specific header of the calendar, default false
18807 * Create a new Container
18808 * @param {Object} config The config object
18813 Roo.bootstrap.Calendar = function(config){
18814 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18818 * Fires when a date is selected
18819 * @param {DatePicker} this
18820 * @param {Date} date The selected date
18824 * @event monthchange
18825 * Fires when the displayed month changes
18826 * @param {DatePicker} this
18827 * @param {Date} date The selected month
18829 'monthchange': true,
18831 * @event evententer
18832 * Fires when mouse over an event
18833 * @param {Calendar} this
18834 * @param {event} Event
18836 'evententer': true,
18838 * @event eventleave
18839 * Fires when the mouse leaves an
18840 * @param {Calendar} this
18843 'eventleave': true,
18845 * @event eventclick
18846 * Fires when the mouse click an
18847 * @param {Calendar} this
18856 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18859 * @cfg {Number} startDay
18860 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18868 getAutoCreate : function(){
18871 var fc_button = function(name, corner, style, content ) {
18872 return Roo.apply({},{
18874 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18876 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18879 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18890 style : 'width:100%',
18897 cls : 'fc-header-left',
18899 fc_button('prev', 'left', 'arrow', '‹' ),
18900 fc_button('next', 'right', 'arrow', '›' ),
18901 { tag: 'span', cls: 'fc-header-space' },
18902 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18910 cls : 'fc-header-center',
18914 cls: 'fc-header-title',
18917 html : 'month / year'
18925 cls : 'fc-header-right',
18927 /* fc_button('month', 'left', '', 'month' ),
18928 fc_button('week', '', '', 'week' ),
18929 fc_button('day', 'right', '', 'day' )
18941 header = this.header;
18944 var cal_heads = function() {
18946 // fixme - handle this.
18948 for (var i =0; i < Date.dayNames.length; i++) {
18949 var d = Date.dayNames[i];
18952 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18953 html : d.substring(0,3)
18957 ret[0].cls += ' fc-first';
18958 ret[6].cls += ' fc-last';
18961 var cal_cell = function(n) {
18964 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18969 cls: 'fc-day-number',
18973 cls: 'fc-day-content',
18977 style: 'position: relative;' // height: 17px;
18989 var cal_rows = function() {
18992 for (var r = 0; r < 6; r++) {
18999 for (var i =0; i < Date.dayNames.length; i++) {
19000 var d = Date.dayNames[i];
19001 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19004 row.cn[0].cls+=' fc-first';
19005 row.cn[0].cn[0].style = 'min-height:90px';
19006 row.cn[6].cls+=' fc-last';
19010 ret[0].cls += ' fc-first';
19011 ret[4].cls += ' fc-prev-last';
19012 ret[5].cls += ' fc-last';
19019 cls: 'fc-border-separate',
19020 style : 'width:100%',
19028 cls : 'fc-first fc-last',
19046 cls : 'fc-content',
19047 style : "position: relative;",
19050 cls : 'fc-view fc-view-month fc-grid',
19051 style : 'position: relative',
19052 unselectable : 'on',
19055 cls : 'fc-event-container',
19056 style : 'position:absolute;z-index:8;top:0;left:0;'
19074 initEvents : function()
19077 throw "can not find store for calendar";
19083 style: "text-align:center",
19087 style: "background-color:white;width:50%;margin:250 auto",
19091 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19102 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19104 var size = this.el.select('.fc-content', true).first().getSize();
19105 this.maskEl.setSize(size.width, size.height);
19106 this.maskEl.enableDisplayMode("block");
19107 if(!this.loadMask){
19108 this.maskEl.hide();
19111 this.store = Roo.factory(this.store, Roo.data);
19112 this.store.on('load', this.onLoad, this);
19113 this.store.on('beforeload', this.onBeforeLoad, this);
19117 this.cells = this.el.select('.fc-day',true);
19118 //Roo.log(this.cells);
19119 this.textNodes = this.el.query('.fc-day-number');
19120 this.cells.addClassOnOver('fc-state-hover');
19122 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19123 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19124 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19125 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19127 this.on('monthchange', this.onMonthChange, this);
19129 this.update(new Date().clearTime());
19132 resize : function() {
19133 var sz = this.el.getSize();
19135 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19136 this.el.select('.fc-day-content div',true).setHeight(34);
19141 showPrevMonth : function(e){
19142 this.update(this.activeDate.add("mo", -1));
19144 showToday : function(e){
19145 this.update(new Date().clearTime());
19148 showNextMonth : function(e){
19149 this.update(this.activeDate.add("mo", 1));
19153 showPrevYear : function(){
19154 this.update(this.activeDate.add("y", -1));
19158 showNextYear : function(){
19159 this.update(this.activeDate.add("y", 1));
19164 update : function(date)
19166 var vd = this.activeDate;
19167 this.activeDate = date;
19168 // if(vd && this.el){
19169 // var t = date.getTime();
19170 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19171 // Roo.log('using add remove');
19173 // this.fireEvent('monthchange', this, date);
19175 // this.cells.removeClass("fc-state-highlight");
19176 // this.cells.each(function(c){
19177 // if(c.dateValue == t){
19178 // c.addClass("fc-state-highlight");
19179 // setTimeout(function(){
19180 // try{c.dom.firstChild.focus();}catch(e){}
19190 var days = date.getDaysInMonth();
19192 var firstOfMonth = date.getFirstDateOfMonth();
19193 var startingPos = firstOfMonth.getDay()-this.startDay;
19195 if(startingPos < this.startDay){
19199 var pm = date.add(Date.MONTH, -1);
19200 var prevStart = pm.getDaysInMonth()-startingPos;
19202 this.cells = this.el.select('.fc-day',true);
19203 this.textNodes = this.el.query('.fc-day-number');
19204 this.cells.addClassOnOver('fc-state-hover');
19206 var cells = this.cells.elements;
19207 var textEls = this.textNodes;
19209 Roo.each(cells, function(cell){
19210 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19213 days += startingPos;
19215 // convert everything to numbers so it's fast
19216 var day = 86400000;
19217 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19220 //Roo.log(prevStart);
19222 var today = new Date().clearTime().getTime();
19223 var sel = date.clearTime().getTime();
19224 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19225 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19226 var ddMatch = this.disabledDatesRE;
19227 var ddText = this.disabledDatesText;
19228 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19229 var ddaysText = this.disabledDaysText;
19230 var format = this.format;
19232 var setCellClass = function(cal, cell){
19236 //Roo.log('set Cell Class');
19238 var t = d.getTime();
19242 cell.dateValue = t;
19244 cell.className += " fc-today";
19245 cell.className += " fc-state-highlight";
19246 cell.title = cal.todayText;
19249 // disable highlight in other month..
19250 //cell.className += " fc-state-highlight";
19255 cell.className = " fc-state-disabled";
19256 cell.title = cal.minText;
19260 cell.className = " fc-state-disabled";
19261 cell.title = cal.maxText;
19265 if(ddays.indexOf(d.getDay()) != -1){
19266 cell.title = ddaysText;
19267 cell.className = " fc-state-disabled";
19270 if(ddMatch && format){
19271 var fvalue = d.dateFormat(format);
19272 if(ddMatch.test(fvalue)){
19273 cell.title = ddText.replace("%0", fvalue);
19274 cell.className = " fc-state-disabled";
19278 if (!cell.initialClassName) {
19279 cell.initialClassName = cell.dom.className;
19282 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19287 for(; i < startingPos; i++) {
19288 textEls[i].innerHTML = (++prevStart);
19289 d.setDate(d.getDate()+1);
19291 cells[i].className = "fc-past fc-other-month";
19292 setCellClass(this, cells[i]);
19297 for(; i < days; i++){
19298 intDay = i - startingPos + 1;
19299 textEls[i].innerHTML = (intDay);
19300 d.setDate(d.getDate()+1);
19302 cells[i].className = ''; // "x-date-active";
19303 setCellClass(this, cells[i]);
19307 for(; i < 42; i++) {
19308 textEls[i].innerHTML = (++extraDays);
19309 d.setDate(d.getDate()+1);
19311 cells[i].className = "fc-future fc-other-month";
19312 setCellClass(this, cells[i]);
19315 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19317 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19319 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19320 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19322 if(totalRows != 6){
19323 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19324 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19327 this.fireEvent('monthchange', this, date);
19331 if(!this.internalRender){
19332 var main = this.el.dom.firstChild;
19333 var w = main.offsetWidth;
19334 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19335 Roo.fly(main).setWidth(w);
19336 this.internalRender = true;
19337 // opera does not respect the auto grow header center column
19338 // then, after it gets a width opera refuses to recalculate
19339 // without a second pass
19340 if(Roo.isOpera && !this.secondPass){
19341 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19342 this.secondPass = true;
19343 this.update.defer(10, this, [date]);
19350 findCell : function(dt) {
19351 dt = dt.clearTime().getTime();
19353 this.cells.each(function(c){
19354 //Roo.log("check " +c.dateValue + '?=' + dt);
19355 if(c.dateValue == dt){
19365 findCells : function(ev) {
19366 var s = ev.start.clone().clearTime().getTime();
19368 var e= ev.end.clone().clearTime().getTime();
19371 this.cells.each(function(c){
19372 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19374 if(c.dateValue > e){
19377 if(c.dateValue < s){
19386 // findBestRow: function(cells)
19390 // for (var i =0 ; i < cells.length;i++) {
19391 // ret = Math.max(cells[i].rows || 0,ret);
19398 addItem : function(ev)
19400 // look for vertical location slot in
19401 var cells = this.findCells(ev);
19403 // ev.row = this.findBestRow(cells);
19405 // work out the location.
19409 for(var i =0; i < cells.length; i++) {
19411 cells[i].row = cells[0].row;
19414 cells[i].row = cells[i].row + 1;
19424 if (crow.start.getY() == cells[i].getY()) {
19426 crow.end = cells[i];
19443 cells[0].events.push(ev);
19445 this.calevents.push(ev);
19448 clearEvents: function() {
19450 if(!this.calevents){
19454 Roo.each(this.cells.elements, function(c){
19460 Roo.each(this.calevents, function(e) {
19461 Roo.each(e.els, function(el) {
19462 el.un('mouseenter' ,this.onEventEnter, this);
19463 el.un('mouseleave' ,this.onEventLeave, this);
19468 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19474 renderEvents: function()
19478 this.cells.each(function(c) {
19487 if(c.row != c.events.length){
19488 r = 4 - (4 - (c.row - c.events.length));
19491 c.events = ev.slice(0, r);
19492 c.more = ev.slice(r);
19494 if(c.more.length && c.more.length == 1){
19495 c.events.push(c.more.pop());
19498 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19502 this.cells.each(function(c) {
19504 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19507 for (var e = 0; e < c.events.length; e++){
19508 var ev = c.events[e];
19509 var rows = ev.rows;
19511 for(var i = 0; i < rows.length; i++) {
19513 // how many rows should it span..
19516 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19517 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19519 unselectable : "on",
19522 cls: 'fc-event-inner',
19526 // cls: 'fc-event-time',
19527 // html : cells.length > 1 ? '' : ev.time
19531 cls: 'fc-event-title',
19532 html : String.format('{0}', ev.title)
19539 cls: 'ui-resizable-handle ui-resizable-e',
19540 html : '  '
19547 cfg.cls += ' fc-event-start';
19549 if ((i+1) == rows.length) {
19550 cfg.cls += ' fc-event-end';
19553 var ctr = _this.el.select('.fc-event-container',true).first();
19554 var cg = ctr.createChild(cfg);
19556 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19557 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19559 var r = (c.more.length) ? 1 : 0;
19560 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19561 cg.setWidth(ebox.right - sbox.x -2);
19563 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19564 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19565 cg.on('click', _this.onEventClick, _this, ev);
19576 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19577 style : 'position: absolute',
19578 unselectable : "on",
19581 cls: 'fc-event-inner',
19585 cls: 'fc-event-title',
19593 cls: 'ui-resizable-handle ui-resizable-e',
19594 html : '  '
19600 var ctr = _this.el.select('.fc-event-container',true).first();
19601 var cg = ctr.createChild(cfg);
19603 var sbox = c.select('.fc-day-content',true).first().getBox();
19604 var ebox = c.select('.fc-day-content',true).first().getBox();
19606 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19607 cg.setWidth(ebox.right - sbox.x -2);
19609 cg.on('click', _this.onMoreEventClick, _this, c.more);
19619 onEventEnter: function (e, el,event,d) {
19620 this.fireEvent('evententer', this, el, event);
19623 onEventLeave: function (e, el,event,d) {
19624 this.fireEvent('eventleave', this, el, event);
19627 onEventClick: function (e, el,event,d) {
19628 this.fireEvent('eventclick', this, el, event);
19631 onMonthChange: function () {
19635 onMoreEventClick: function(e, el, more)
19639 this.calpopover.placement = 'right';
19640 this.calpopover.setTitle('More');
19642 this.calpopover.setContent('');
19644 var ctr = this.calpopover.el.select('.popover-content', true).first();
19646 Roo.each(more, function(m){
19648 cls : 'fc-event-hori fc-event-draggable',
19651 var cg = ctr.createChild(cfg);
19653 cg.on('click', _this.onEventClick, _this, m);
19656 this.calpopover.show(el);
19661 onLoad: function ()
19663 this.calevents = [];
19666 if(this.store.getCount() > 0){
19667 this.store.data.each(function(d){
19670 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19671 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19672 time : d.data.start_time,
19673 title : d.data.title,
19674 description : d.data.description,
19675 venue : d.data.venue
19680 this.renderEvents();
19682 if(this.calevents.length && this.loadMask){
19683 this.maskEl.hide();
19687 onBeforeLoad: function()
19689 this.clearEvents();
19691 this.maskEl.show();
19705 * @class Roo.bootstrap.Popover
19706 * @extends Roo.bootstrap.Component
19707 * Bootstrap Popover class
19708 * @cfg {String} html contents of the popover (or false to use children..)
19709 * @cfg {String} title of popover (or false to hide)
19710 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19711 * @cfg {String} trigger click || hover (or false to trigger manually)
19712 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19713 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19714 * - if false and it has a 'parent' then it will be automatically added to that element
19715 * - if string - Roo.get will be called
19716 * @cfg {Number} delay - delay before showing
19719 * Create a new Popover
19720 * @param {Object} config The config object
19723 Roo.bootstrap.Popover = function(config){
19724 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19730 * After the popover show
19732 * @param {Roo.bootstrap.Popover} this
19737 * After the popover hide
19739 * @param {Roo.bootstrap.Popover} this
19745 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19750 placement : 'right',
19751 trigger : 'hover', // hover
19757 can_build_overlaid : false,
19759 maskEl : false, // the mask element
19762 alignEl : false, // when show is called with an element - this get's stored.
19764 getChildContainer : function()
19766 return this.contentEl;
19769 getPopoverHeader : function()
19771 this.title = true; // flag not to hide it..
19772 this.headerEl.addClass('p-0');
19773 return this.headerEl
19777 getAutoCreate : function(){
19780 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19781 style: 'display:block',
19787 cls : 'popover-inner ',
19791 cls: 'popover-title popover-header',
19792 html : this.title === false ? '' : this.title
19795 cls : 'popover-content popover-body ' + (this.cls || ''),
19796 html : this.html || ''
19807 * @param {string} the title
19809 setTitle: function(str)
19813 this.headerEl.dom.innerHTML = str;
19818 * @param {string} the body content
19820 setContent: function(str)
19823 if (this.contentEl) {
19824 this.contentEl.dom.innerHTML = str;
19828 // as it get's added to the bottom of the page.
19829 onRender : function(ct, position)
19831 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19836 var cfg = Roo.apply({}, this.getAutoCreate());
19840 cfg.cls += ' ' + this.cls;
19843 cfg.style = this.style;
19845 //Roo.log("adding to ");
19846 this.el = Roo.get(document.body).createChild(cfg, position);
19847 // Roo.log(this.el);
19850 this.contentEl = this.el.select('.popover-content',true).first();
19851 this.headerEl = this.el.select('.popover-title',true).first();
19854 if(typeof(this.items) != 'undefined'){
19855 var items = this.items;
19858 for(var i =0;i < items.length;i++) {
19859 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19863 this.items = nitems;
19865 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19866 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19873 resizeMask : function()
19875 this.maskEl.setSize(
19876 Roo.lib.Dom.getViewWidth(true),
19877 Roo.lib.Dom.getViewHeight(true)
19881 initEvents : function()
19885 Roo.bootstrap.Popover.register(this);
19888 this.arrowEl = this.el.select('.arrow',true).first();
19889 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19890 this.el.enableDisplayMode('block');
19894 if (this.over === false && !this.parent()) {
19897 if (this.triggers === false) {
19902 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19903 var triggers = this.trigger ? this.trigger.split(' ') : [];
19904 Roo.each(triggers, function(trigger) {
19906 if (trigger == 'click') {
19907 on_el.on('click', this.toggle, this);
19908 } else if (trigger != 'manual') {
19909 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19910 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19912 on_el.on(eventIn ,this.enter, this);
19913 on_el.on(eventOut, this.leave, this);
19923 toggle : function () {
19924 this.hoverState == 'in' ? this.leave() : this.enter();
19927 enter : function () {
19929 clearTimeout(this.timeout);
19931 this.hoverState = 'in';
19933 if (!this.delay || !this.delay.show) {
19938 this.timeout = setTimeout(function () {
19939 if (_t.hoverState == 'in') {
19942 }, this.delay.show)
19945 leave : function() {
19946 clearTimeout(this.timeout);
19948 this.hoverState = 'out';
19950 if (!this.delay || !this.delay.hide) {
19955 this.timeout = setTimeout(function () {
19956 if (_t.hoverState == 'out') {
19959 }, this.delay.hide)
19963 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19964 * @param {string} (left|right|top|bottom) position
19966 show : function (on_el, placement)
19968 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19969 on_el = on_el || false; // default to false
19972 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19973 on_el = this.parent().el;
19974 } else if (this.over) {
19975 Roo.get(this.over);
19980 this.alignEl = Roo.get( on_el );
19983 this.render(document.body);
19989 if (this.title === false) {
19990 this.headerEl.hide();
19995 this.el.dom.style.display = 'block';
19998 if (this.alignEl) {
19999 this.updatePosition(this.placement, true);
20002 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20003 var es = this.el.getSize();
20004 var x = Roo.lib.Dom.getViewWidth()/2;
20005 var y = Roo.lib.Dom.getViewHeight()/2;
20006 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20011 //var arrow = this.el.select('.arrow',true).first();
20012 //arrow.set(align[2],
20014 this.el.addClass('in');
20018 this.hoverState = 'in';
20021 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20022 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20023 this.maskEl.dom.style.display = 'block';
20024 this.maskEl.addClass('show');
20026 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20028 this.fireEvent('show', this);
20032 * fire this manually after loading a grid in the table for example
20033 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20034 * @param {Boolean} try and move it if we cant get right position.
20036 updatePosition : function(placement, try_move)
20038 // allow for calling with no parameters
20039 placement = placement ? placement : this.placement;
20040 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20042 this.el.removeClass([
20043 'fade','top','bottom', 'left', 'right','in',
20044 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20046 this.el.addClass(placement + ' bs-popover-' + placement);
20048 if (!this.alignEl ) {
20052 switch (placement) {
20054 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20055 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20056 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20057 //normal display... or moved up/down.
20058 this.el.setXY(offset);
20059 var xy = this.alignEl.getAnchorXY('tr', false);
20061 this.arrowEl.setXY(xy);
20064 // continue through...
20065 return this.updatePosition('left', false);
20069 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20070 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20071 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20072 //normal display... or moved up/down.
20073 this.el.setXY(offset);
20074 var xy = this.alignEl.getAnchorXY('tl', false);
20075 xy[0]-=10;xy[1]+=5; // << fix me
20076 this.arrowEl.setXY(xy);
20080 return this.updatePosition('right', false);
20083 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20084 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20085 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20086 //normal display... or moved up/down.
20087 this.el.setXY(offset);
20088 var xy = this.alignEl.getAnchorXY('t', false);
20089 xy[1]-=10; // << fix me
20090 this.arrowEl.setXY(xy);
20094 return this.updatePosition('bottom', false);
20097 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20098 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20099 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20100 //normal display... or moved up/down.
20101 this.el.setXY(offset);
20102 var xy = this.alignEl.getAnchorXY('b', false);
20103 xy[1]+=2; // << fix me
20104 this.arrowEl.setXY(xy);
20108 return this.updatePosition('top', false);
20119 this.el.setXY([0,0]);
20120 this.el.removeClass('in');
20122 this.hoverState = null;
20123 this.maskEl.hide(); // always..
20124 this.fireEvent('hide', this);
20130 Roo.apply(Roo.bootstrap.Popover, {
20133 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20134 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20135 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20136 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20141 clickHander : false,
20144 onMouseDown : function(e)
20146 if (!e.getTarget(".roo-popover")) {
20154 register : function(popup)
20156 if (!Roo.bootstrap.Popover.clickHandler) {
20157 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20159 // hide other popups.
20161 this.popups.push(popup);
20163 hideAll : function()
20165 this.popups.forEach(function(p) {
20173 * Card header - holder for the card header elements.
20178 * @class Roo.bootstrap.PopoverNav
20179 * @extends Roo.bootstrap.NavGroup
20180 * Bootstrap Popover header navigation class
20182 * Create a new Popover Header Navigation
20183 * @param {Object} config The config object
20186 Roo.bootstrap.PopoverNav = function(config){
20187 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20190 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20193 container_method : 'getPopoverHeader'
20211 * @class Roo.bootstrap.Progress
20212 * @extends Roo.bootstrap.Component
20213 * Bootstrap Progress class
20214 * @cfg {Boolean} striped striped of the progress bar
20215 * @cfg {Boolean} active animated of the progress bar
20219 * Create a new Progress
20220 * @param {Object} config The config object
20223 Roo.bootstrap.Progress = function(config){
20224 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20227 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20232 getAutoCreate : function(){
20240 cfg.cls += ' progress-striped';
20244 cfg.cls += ' active';
20263 * @class Roo.bootstrap.ProgressBar
20264 * @extends Roo.bootstrap.Component
20265 * Bootstrap ProgressBar class
20266 * @cfg {Number} aria_valuenow aria-value now
20267 * @cfg {Number} aria_valuemin aria-value min
20268 * @cfg {Number} aria_valuemax aria-value max
20269 * @cfg {String} label label for the progress bar
20270 * @cfg {String} panel (success | info | warning | danger )
20271 * @cfg {String} role role of the progress bar
20272 * @cfg {String} sr_only text
20276 * Create a new ProgressBar
20277 * @param {Object} config The config object
20280 Roo.bootstrap.ProgressBar = function(config){
20281 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20284 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20288 aria_valuemax : 100,
20294 getAutoCreate : function()
20299 cls: 'progress-bar',
20300 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20312 cfg.role = this.role;
20315 if(this.aria_valuenow){
20316 cfg['aria-valuenow'] = this.aria_valuenow;
20319 if(this.aria_valuemin){
20320 cfg['aria-valuemin'] = this.aria_valuemin;
20323 if(this.aria_valuemax){
20324 cfg['aria-valuemax'] = this.aria_valuemax;
20327 if(this.label && !this.sr_only){
20328 cfg.html = this.label;
20332 cfg.cls += ' progress-bar-' + this.panel;
20338 update : function(aria_valuenow)
20340 this.aria_valuenow = aria_valuenow;
20342 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20357 * @class Roo.bootstrap.TabGroup
20358 * @extends Roo.bootstrap.Column
20359 * Bootstrap Column class
20360 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20361 * @cfg {Boolean} carousel true to make the group behave like a carousel
20362 * @cfg {Boolean} bullets show bullets for the panels
20363 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20364 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20365 * @cfg {Boolean} showarrow (true|false) show arrow default true
20368 * Create a new TabGroup
20369 * @param {Object} config The config object
20372 Roo.bootstrap.TabGroup = function(config){
20373 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20375 this.navId = Roo.id();
20378 Roo.bootstrap.TabGroup.register(this);
20382 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20385 transition : false,
20390 slideOnTouch : false,
20393 getAutoCreate : function()
20395 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20397 cfg.cls += ' tab-content';
20399 if (this.carousel) {
20400 cfg.cls += ' carousel slide';
20403 cls : 'carousel-inner',
20407 if(this.bullets && !Roo.isTouch){
20410 cls : 'carousel-bullets',
20414 if(this.bullets_cls){
20415 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20422 cfg.cn[0].cn.push(bullets);
20425 if(this.showarrow){
20426 cfg.cn[0].cn.push({
20428 class : 'carousel-arrow',
20432 class : 'carousel-prev',
20436 class : 'fa fa-chevron-left'
20442 class : 'carousel-next',
20446 class : 'fa fa-chevron-right'
20459 initEvents: function()
20461 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20462 // this.el.on("touchstart", this.onTouchStart, this);
20465 if(this.autoslide){
20468 this.slideFn = window.setInterval(function() {
20469 _this.showPanelNext();
20473 if(this.showarrow){
20474 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20475 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20481 // onTouchStart : function(e, el, o)
20483 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20487 // this.showPanelNext();
20491 getChildContainer : function()
20493 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20497 * register a Navigation item
20498 * @param {Roo.bootstrap.NavItem} the navitem to add
20500 register : function(item)
20502 this.tabs.push( item);
20503 item.navId = this.navId; // not really needed..
20508 getActivePanel : function()
20511 Roo.each(this.tabs, function(t) {
20521 getPanelByName : function(n)
20524 Roo.each(this.tabs, function(t) {
20525 if (t.tabId == n) {
20533 indexOfPanel : function(p)
20536 Roo.each(this.tabs, function(t,i) {
20537 if (t.tabId == p.tabId) {
20546 * show a specific panel
20547 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20548 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20550 showPanel : function (pan)
20552 if(this.transition || typeof(pan) == 'undefined'){
20553 Roo.log("waiting for the transitionend");
20557 if (typeof(pan) == 'number') {
20558 pan = this.tabs[pan];
20561 if (typeof(pan) == 'string') {
20562 pan = this.getPanelByName(pan);
20565 var cur = this.getActivePanel();
20568 Roo.log('pan or acitve pan is undefined');
20572 if (pan.tabId == this.getActivePanel().tabId) {
20576 if (false === cur.fireEvent('beforedeactivate')) {
20580 if(this.bullets > 0 && !Roo.isTouch){
20581 this.setActiveBullet(this.indexOfPanel(pan));
20584 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20586 //class="carousel-item carousel-item-next carousel-item-left"
20588 this.transition = true;
20589 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20590 var lr = dir == 'next' ? 'left' : 'right';
20591 pan.el.addClass(dir); // or prev
20592 pan.el.addClass('carousel-item-' + dir); // or prev
20593 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20594 cur.el.addClass(lr); // or right
20595 pan.el.addClass(lr);
20596 cur.el.addClass('carousel-item-' +lr); // or right
20597 pan.el.addClass('carousel-item-' +lr);
20601 cur.el.on('transitionend', function() {
20602 Roo.log("trans end?");
20604 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20605 pan.setActive(true);
20607 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20608 cur.setActive(false);
20610 _this.transition = false;
20612 }, this, { single: true } );
20617 cur.setActive(false);
20618 pan.setActive(true);
20623 showPanelNext : function()
20625 var i = this.indexOfPanel(this.getActivePanel());
20627 if (i >= this.tabs.length - 1 && !this.autoslide) {
20631 if (i >= this.tabs.length - 1 && this.autoslide) {
20635 this.showPanel(this.tabs[i+1]);
20638 showPanelPrev : function()
20640 var i = this.indexOfPanel(this.getActivePanel());
20642 if (i < 1 && !this.autoslide) {
20646 if (i < 1 && this.autoslide) {
20647 i = this.tabs.length;
20650 this.showPanel(this.tabs[i-1]);
20654 addBullet: function()
20656 if(!this.bullets || Roo.isTouch){
20659 var ctr = this.el.select('.carousel-bullets',true).first();
20660 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20661 var bullet = ctr.createChild({
20662 cls : 'bullet bullet-' + i
20663 },ctr.dom.lastChild);
20668 bullet.on('click', (function(e, el, o, ii, t){
20670 e.preventDefault();
20672 this.showPanel(ii);
20674 if(this.autoslide && this.slideFn){
20675 clearInterval(this.slideFn);
20676 this.slideFn = window.setInterval(function() {
20677 _this.showPanelNext();
20681 }).createDelegate(this, [i, bullet], true));
20686 setActiveBullet : function(i)
20692 Roo.each(this.el.select('.bullet', true).elements, function(el){
20693 el.removeClass('selected');
20696 var bullet = this.el.select('.bullet-' + i, true).first();
20702 bullet.addClass('selected');
20713 Roo.apply(Roo.bootstrap.TabGroup, {
20717 * register a Navigation Group
20718 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20720 register : function(navgrp)
20722 this.groups[navgrp.navId] = navgrp;
20726 * fetch a Navigation Group based on the navigation ID
20727 * if one does not exist , it will get created.
20728 * @param {string} the navgroup to add
20729 * @returns {Roo.bootstrap.NavGroup} the navgroup
20731 get: function(navId) {
20732 if (typeof(this.groups[navId]) == 'undefined') {
20733 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20735 return this.groups[navId] ;
20750 * @class Roo.bootstrap.TabPanel
20751 * @extends Roo.bootstrap.Component
20752 * Bootstrap TabPanel class
20753 * @cfg {Boolean} active panel active
20754 * @cfg {String} html panel content
20755 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20756 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20757 * @cfg {String} href click to link..
20758 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20762 * Create a new TabPanel
20763 * @param {Object} config The config object
20766 Roo.bootstrap.TabPanel = function(config){
20767 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20771 * Fires when the active status changes
20772 * @param {Roo.bootstrap.TabPanel} this
20773 * @param {Boolean} state the new state
20778 * @event beforedeactivate
20779 * Fires before a tab is de-activated - can be used to do validation on a form.
20780 * @param {Roo.bootstrap.TabPanel} this
20781 * @return {Boolean} false if there is an error
20784 'beforedeactivate': true
20787 this.tabId = this.tabId || Roo.id();
20791 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20798 touchSlide : false,
20799 getAutoCreate : function(){
20804 // item is needed for carousel - not sure if it has any effect otherwise
20805 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20806 html: this.html || ''
20810 cfg.cls += ' active';
20814 cfg.tabId = this.tabId;
20822 initEvents: function()
20824 var p = this.parent();
20826 this.navId = this.navId || p.navId;
20828 if (typeof(this.navId) != 'undefined') {
20829 // not really needed.. but just in case.. parent should be a NavGroup.
20830 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20834 var i = tg.tabs.length - 1;
20836 if(this.active && tg.bullets > 0 && i < tg.bullets){
20837 tg.setActiveBullet(i);
20841 this.el.on('click', this.onClick, this);
20843 if(Roo.isTouch && this.touchSlide){
20844 this.el.on("touchstart", this.onTouchStart, this);
20845 this.el.on("touchmove", this.onTouchMove, this);
20846 this.el.on("touchend", this.onTouchEnd, this);
20851 onRender : function(ct, position)
20853 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20856 setActive : function(state)
20858 Roo.log("panel - set active " + this.tabId + "=" + state);
20860 this.active = state;
20862 this.el.removeClass('active');
20864 } else if (!this.el.hasClass('active')) {
20865 this.el.addClass('active');
20868 this.fireEvent('changed', this, state);
20871 onClick : function(e)
20873 e.preventDefault();
20875 if(!this.href.length){
20879 window.location.href = this.href;
20888 onTouchStart : function(e)
20890 this.swiping = false;
20892 this.startX = e.browserEvent.touches[0].clientX;
20893 this.startY = e.browserEvent.touches[0].clientY;
20896 onTouchMove : function(e)
20898 this.swiping = true;
20900 this.endX = e.browserEvent.touches[0].clientX;
20901 this.endY = e.browserEvent.touches[0].clientY;
20904 onTouchEnd : function(e)
20911 var tabGroup = this.parent();
20913 if(this.endX > this.startX){ // swiping right
20914 tabGroup.showPanelPrev();
20918 if(this.startX > this.endX){ // swiping left
20919 tabGroup.showPanelNext();
20938 * @class Roo.bootstrap.DateField
20939 * @extends Roo.bootstrap.Input
20940 * Bootstrap DateField class
20941 * @cfg {Number} weekStart default 0
20942 * @cfg {String} viewMode default empty, (months|years)
20943 * @cfg {String} minViewMode default empty, (months|years)
20944 * @cfg {Number} startDate default -Infinity
20945 * @cfg {Number} endDate default Infinity
20946 * @cfg {Boolean} todayHighlight default false
20947 * @cfg {Boolean} todayBtn default false
20948 * @cfg {Boolean} calendarWeeks default false
20949 * @cfg {Object} daysOfWeekDisabled default empty
20950 * @cfg {Boolean} singleMode default false (true | false)
20952 * @cfg {Boolean} keyboardNavigation default true
20953 * @cfg {String} language default en
20956 * Create a new DateField
20957 * @param {Object} config The config object
20960 Roo.bootstrap.DateField = function(config){
20961 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20965 * Fires when this field show.
20966 * @param {Roo.bootstrap.DateField} this
20967 * @param {Mixed} date The date value
20972 * Fires when this field hide.
20973 * @param {Roo.bootstrap.DateField} this
20974 * @param {Mixed} date The date value
20979 * Fires when select a date.
20980 * @param {Roo.bootstrap.DateField} this
20981 * @param {Mixed} date The date value
20985 * @event beforeselect
20986 * Fires when before select a date.
20987 * @param {Roo.bootstrap.DateField} this
20988 * @param {Mixed} date The date value
20990 beforeselect : true
20994 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20997 * @cfg {String} format
20998 * The default date format string which can be overriden for localization support. The format must be
20999 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21003 * @cfg {String} altFormats
21004 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21005 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21007 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21015 todayHighlight : false,
21021 keyboardNavigation: true,
21023 calendarWeeks: false,
21025 startDate: -Infinity,
21029 daysOfWeekDisabled: [],
21033 singleMode : false,
21035 UTCDate: function()
21037 return new Date(Date.UTC.apply(Date, arguments));
21040 UTCToday: function()
21042 var today = new Date();
21043 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21046 getDate: function() {
21047 var d = this.getUTCDate();
21048 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21051 getUTCDate: function() {
21055 setDate: function(d) {
21056 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21059 setUTCDate: function(d) {
21061 this.setValue(this.formatDate(this.date));
21064 onRender: function(ct, position)
21067 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21069 this.language = this.language || 'en';
21070 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21071 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21073 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21074 this.format = this.format || 'm/d/y';
21075 this.isInline = false;
21076 this.isInput = true;
21077 this.component = this.el.select('.add-on', true).first() || false;
21078 this.component = (this.component && this.component.length === 0) ? false : this.component;
21079 this.hasInput = this.component && this.inputEl().length;
21081 if (typeof(this.minViewMode === 'string')) {
21082 switch (this.minViewMode) {
21084 this.minViewMode = 1;
21087 this.minViewMode = 2;
21090 this.minViewMode = 0;
21095 if (typeof(this.viewMode === 'string')) {
21096 switch (this.viewMode) {
21109 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21111 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21113 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21115 this.picker().on('mousedown', this.onMousedown, this);
21116 this.picker().on('click', this.onClick, this);
21118 this.picker().addClass('datepicker-dropdown');
21120 this.startViewMode = this.viewMode;
21122 if(this.singleMode){
21123 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21124 v.setVisibilityMode(Roo.Element.DISPLAY);
21128 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21129 v.setStyle('width', '189px');
21133 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21134 if(!this.calendarWeeks){
21139 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21140 v.attr('colspan', function(i, val){
21141 return parseInt(val) + 1;
21146 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21148 this.setStartDate(this.startDate);
21149 this.setEndDate(this.endDate);
21151 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21158 if(this.isInline) {
21163 picker : function()
21165 return this.pickerEl;
21166 // return this.el.select('.datepicker', true).first();
21169 fillDow: function()
21171 var dowCnt = this.weekStart;
21180 if(this.calendarWeeks){
21188 while (dowCnt < this.weekStart + 7) {
21192 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21196 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21199 fillMonths: function()
21202 var months = this.picker().select('>.datepicker-months td', true).first();
21204 months.dom.innerHTML = '';
21210 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21213 months.createChild(month);
21220 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;
21222 if (this.date < this.startDate) {
21223 this.viewDate = new Date(this.startDate);
21224 } else if (this.date > this.endDate) {
21225 this.viewDate = new Date(this.endDate);
21227 this.viewDate = new Date(this.date);
21235 var d = new Date(this.viewDate),
21236 year = d.getUTCFullYear(),
21237 month = d.getUTCMonth(),
21238 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21239 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21240 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21241 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21242 currentDate = this.date && this.date.valueOf(),
21243 today = this.UTCToday();
21245 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21247 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21249 // this.picker.select('>tfoot th.today').
21250 // .text(dates[this.language].today)
21251 // .toggle(this.todayBtn !== false);
21253 this.updateNavArrows();
21256 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21258 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21260 prevMonth.setUTCDate(day);
21262 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21264 var nextMonth = new Date(prevMonth);
21266 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21268 nextMonth = nextMonth.valueOf();
21270 var fillMonths = false;
21272 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21274 while(prevMonth.valueOf() <= nextMonth) {
21277 if (prevMonth.getUTCDay() === this.weekStart) {
21279 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21287 if(this.calendarWeeks){
21288 // ISO 8601: First week contains first thursday.
21289 // ISO also states week starts on Monday, but we can be more abstract here.
21291 // Start of current week: based on weekstart/current date
21292 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21293 // Thursday of this week
21294 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21295 // First Thursday of year, year from thursday
21296 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21297 // Calendar week: ms between thursdays, div ms per day, div 7 days
21298 calWeek = (th - yth) / 864e5 / 7 + 1;
21300 fillMonths.cn.push({
21308 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21310 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21313 if (this.todayHighlight &&
21314 prevMonth.getUTCFullYear() == today.getFullYear() &&
21315 prevMonth.getUTCMonth() == today.getMonth() &&
21316 prevMonth.getUTCDate() == today.getDate()) {
21317 clsName += ' today';
21320 if (currentDate && prevMonth.valueOf() === currentDate) {
21321 clsName += ' active';
21324 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21325 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21326 clsName += ' disabled';
21329 fillMonths.cn.push({
21331 cls: 'day ' + clsName,
21332 html: prevMonth.getDate()
21335 prevMonth.setDate(prevMonth.getDate()+1);
21338 var currentYear = this.date && this.date.getUTCFullYear();
21339 var currentMonth = this.date && this.date.getUTCMonth();
21341 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21343 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21344 v.removeClass('active');
21346 if(currentYear === year && k === currentMonth){
21347 v.addClass('active');
21350 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21351 v.addClass('disabled');
21357 year = parseInt(year/10, 10) * 10;
21359 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21361 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21364 for (var i = -1; i < 11; i++) {
21365 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21367 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21375 showMode: function(dir)
21378 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21381 Roo.each(this.picker().select('>div',true).elements, function(v){
21382 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21385 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21390 if(this.isInline) {
21394 this.picker().removeClass(['bottom', 'top']);
21396 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21398 * place to the top of element!
21402 this.picker().addClass('top');
21403 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21408 this.picker().addClass('bottom');
21410 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21413 parseDate : function(value)
21415 if(!value || value instanceof Date){
21418 var v = Date.parseDate(value, this.format);
21419 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21420 v = Date.parseDate(value, 'Y-m-d');
21422 if(!v && this.altFormats){
21423 if(!this.altFormatsArray){
21424 this.altFormatsArray = this.altFormats.split("|");
21426 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21427 v = Date.parseDate(value, this.altFormatsArray[i]);
21433 formatDate : function(date, fmt)
21435 return (!date || !(date instanceof Date)) ?
21436 date : date.dateFormat(fmt || this.format);
21439 onFocus : function()
21441 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21445 onBlur : function()
21447 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21449 var d = this.inputEl().getValue();
21456 showPopup : function()
21458 this.picker().show();
21462 this.fireEvent('showpopup', this, this.date);
21465 hidePopup : function()
21467 if(this.isInline) {
21470 this.picker().hide();
21471 this.viewMode = this.startViewMode;
21474 this.fireEvent('hidepopup', this, this.date);
21478 onMousedown: function(e)
21480 e.stopPropagation();
21481 e.preventDefault();
21486 Roo.bootstrap.DateField.superclass.keyup.call(this);
21490 setValue: function(v)
21492 if(this.fireEvent('beforeselect', this, v) !== false){
21493 var d = new Date(this.parseDate(v) ).clearTime();
21495 if(isNaN(d.getTime())){
21496 this.date = this.viewDate = '';
21497 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21501 v = this.formatDate(d);
21503 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21505 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21509 this.fireEvent('select', this, this.date);
21513 getValue: function()
21515 return this.formatDate(this.date);
21518 fireKey: function(e)
21520 if (!this.picker().isVisible()){
21521 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21527 var dateChanged = false,
21529 newDate, newViewDate;
21534 e.preventDefault();
21538 if (!this.keyboardNavigation) {
21541 dir = e.keyCode == 37 ? -1 : 1;
21544 newDate = this.moveYear(this.date, dir);
21545 newViewDate = this.moveYear(this.viewDate, dir);
21546 } else if (e.shiftKey){
21547 newDate = this.moveMonth(this.date, dir);
21548 newViewDate = this.moveMonth(this.viewDate, dir);
21550 newDate = new Date(this.date);
21551 newDate.setUTCDate(this.date.getUTCDate() + dir);
21552 newViewDate = new Date(this.viewDate);
21553 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21555 if (this.dateWithinRange(newDate)){
21556 this.date = newDate;
21557 this.viewDate = newViewDate;
21558 this.setValue(this.formatDate(this.date));
21560 e.preventDefault();
21561 dateChanged = true;
21566 if (!this.keyboardNavigation) {
21569 dir = e.keyCode == 38 ? -1 : 1;
21571 newDate = this.moveYear(this.date, dir);
21572 newViewDate = this.moveYear(this.viewDate, dir);
21573 } else if (e.shiftKey){
21574 newDate = this.moveMonth(this.date, dir);
21575 newViewDate = this.moveMonth(this.viewDate, dir);
21577 newDate = new Date(this.date);
21578 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21579 newViewDate = new Date(this.viewDate);
21580 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21582 if (this.dateWithinRange(newDate)){
21583 this.date = newDate;
21584 this.viewDate = newViewDate;
21585 this.setValue(this.formatDate(this.date));
21587 e.preventDefault();
21588 dateChanged = true;
21592 this.setValue(this.formatDate(this.date));
21594 e.preventDefault();
21597 this.setValue(this.formatDate(this.date));
21611 onClick: function(e)
21613 e.stopPropagation();
21614 e.preventDefault();
21616 var target = e.getTarget();
21618 if(target.nodeName.toLowerCase() === 'i'){
21619 target = Roo.get(target).dom.parentNode;
21622 var nodeName = target.nodeName;
21623 var className = target.className;
21624 var html = target.innerHTML;
21625 //Roo.log(nodeName);
21627 switch(nodeName.toLowerCase()) {
21629 switch(className) {
21635 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21636 switch(this.viewMode){
21638 this.viewDate = this.moveMonth(this.viewDate, dir);
21642 this.viewDate = this.moveYear(this.viewDate, dir);
21648 var date = new Date();
21649 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21651 this.setValue(this.formatDate(this.date));
21658 if (className.indexOf('disabled') < 0) {
21659 this.viewDate.setUTCDate(1);
21660 if (className.indexOf('month') > -1) {
21661 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21663 var year = parseInt(html, 10) || 0;
21664 this.viewDate.setUTCFullYear(year);
21668 if(this.singleMode){
21669 this.setValue(this.formatDate(this.viewDate));
21680 //Roo.log(className);
21681 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21682 var day = parseInt(html, 10) || 1;
21683 var year = (this.viewDate || new Date()).getUTCFullYear(),
21684 month = (this.viewDate || new Date()).getUTCMonth();
21686 if (className.indexOf('old') > -1) {
21693 } else if (className.indexOf('new') > -1) {
21701 //Roo.log([year,month,day]);
21702 this.date = this.UTCDate(year, month, day,0,0,0,0);
21703 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21705 //Roo.log(this.formatDate(this.date));
21706 this.setValue(this.formatDate(this.date));
21713 setStartDate: function(startDate)
21715 this.startDate = startDate || -Infinity;
21716 if (this.startDate !== -Infinity) {
21717 this.startDate = this.parseDate(this.startDate);
21720 this.updateNavArrows();
21723 setEndDate: function(endDate)
21725 this.endDate = endDate || Infinity;
21726 if (this.endDate !== Infinity) {
21727 this.endDate = this.parseDate(this.endDate);
21730 this.updateNavArrows();
21733 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21735 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21736 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21737 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21739 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21740 return parseInt(d, 10);
21743 this.updateNavArrows();
21746 updateNavArrows: function()
21748 if(this.singleMode){
21752 var d = new Date(this.viewDate),
21753 year = d.getUTCFullYear(),
21754 month = d.getUTCMonth();
21756 Roo.each(this.picker().select('.prev', true).elements, function(v){
21758 switch (this.viewMode) {
21761 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21767 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21774 Roo.each(this.picker().select('.next', true).elements, function(v){
21776 switch (this.viewMode) {
21779 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21785 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21793 moveMonth: function(date, dir)
21798 var new_date = new Date(date.valueOf()),
21799 day = new_date.getUTCDate(),
21800 month = new_date.getUTCMonth(),
21801 mag = Math.abs(dir),
21803 dir = dir > 0 ? 1 : -1;
21806 // If going back one month, make sure month is not current month
21807 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21809 return new_date.getUTCMonth() == month;
21811 // If going forward one month, make sure month is as expected
21812 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21814 return new_date.getUTCMonth() != new_month;
21816 new_month = month + dir;
21817 new_date.setUTCMonth(new_month);
21818 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21819 if (new_month < 0 || new_month > 11) {
21820 new_month = (new_month + 12) % 12;
21823 // For magnitudes >1, move one month at a time...
21824 for (var i=0; i<mag; i++) {
21825 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21826 new_date = this.moveMonth(new_date, dir);
21828 // ...then reset the day, keeping it in the new month
21829 new_month = new_date.getUTCMonth();
21830 new_date.setUTCDate(day);
21832 return new_month != new_date.getUTCMonth();
21835 // Common date-resetting loop -- if date is beyond end of month, make it
21838 new_date.setUTCDate(--day);
21839 new_date.setUTCMonth(new_month);
21844 moveYear: function(date, dir)
21846 return this.moveMonth(date, dir*12);
21849 dateWithinRange: function(date)
21851 return date >= this.startDate && date <= this.endDate;
21857 this.picker().remove();
21860 validateValue : function(value)
21862 if(this.getVisibilityEl().hasClass('hidden')){
21866 if(value.length < 1) {
21867 if(this.allowBlank){
21873 if(value.length < this.minLength){
21876 if(value.length > this.maxLength){
21880 var vt = Roo.form.VTypes;
21881 if(!vt[this.vtype](value, this)){
21885 if(typeof this.validator == "function"){
21886 var msg = this.validator(value);
21892 if(this.regex && !this.regex.test(value)){
21896 if(typeof(this.parseDate(value)) == 'undefined'){
21900 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21904 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21914 this.date = this.viewDate = '';
21916 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21921 Roo.apply(Roo.bootstrap.DateField, {
21932 html: '<i class="fa fa-arrow-left"/>'
21942 html: '<i class="fa fa-arrow-right"/>'
21984 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21985 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21986 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21987 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21988 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22001 navFnc: 'FullYear',
22006 navFnc: 'FullYear',
22011 Roo.apply(Roo.bootstrap.DateField, {
22015 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22019 cls: 'datepicker-days',
22023 cls: 'table-condensed',
22025 Roo.bootstrap.DateField.head,
22029 Roo.bootstrap.DateField.footer
22036 cls: 'datepicker-months',
22040 cls: 'table-condensed',
22042 Roo.bootstrap.DateField.head,
22043 Roo.bootstrap.DateField.content,
22044 Roo.bootstrap.DateField.footer
22051 cls: 'datepicker-years',
22055 cls: 'table-condensed',
22057 Roo.bootstrap.DateField.head,
22058 Roo.bootstrap.DateField.content,
22059 Roo.bootstrap.DateField.footer
22078 * @class Roo.bootstrap.TimeField
22079 * @extends Roo.bootstrap.Input
22080 * Bootstrap DateField class
22084 * Create a new TimeField
22085 * @param {Object} config The config object
22088 Roo.bootstrap.TimeField = function(config){
22089 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22093 * Fires when this field show.
22094 * @param {Roo.bootstrap.DateField} thisthis
22095 * @param {Mixed} date The date value
22100 * Fires when this field hide.
22101 * @param {Roo.bootstrap.DateField} this
22102 * @param {Mixed} date The date value
22107 * Fires when select a date.
22108 * @param {Roo.bootstrap.DateField} this
22109 * @param {Mixed} date The date value
22115 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22118 * @cfg {String} format
22119 * The default time format string which can be overriden for localization support. The format must be
22120 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22124 getAutoCreate : function()
22126 this.after = '<i class="fa far fa-clock"></i>';
22127 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22131 onRender: function(ct, position)
22134 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22136 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22138 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22140 this.pop = this.picker().select('>.datepicker-time',true).first();
22141 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22143 this.picker().on('mousedown', this.onMousedown, this);
22144 this.picker().on('click', this.onClick, this);
22146 this.picker().addClass('datepicker-dropdown');
22151 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22152 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22153 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22154 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22155 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22156 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22160 fireKey: function(e){
22161 if (!this.picker().isVisible()){
22162 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22168 e.preventDefault();
22176 this.onTogglePeriod();
22179 this.onIncrementMinutes();
22182 this.onDecrementMinutes();
22191 onClick: function(e) {
22192 e.stopPropagation();
22193 e.preventDefault();
22196 picker : function()
22198 return this.pickerEl;
22201 fillTime: function()
22203 var time = this.pop.select('tbody', true).first();
22205 time.dom.innerHTML = '';
22220 cls: 'hours-up fa fas fa-chevron-up'
22240 cls: 'minutes-up fa fas fa-chevron-up'
22261 cls: 'timepicker-hour',
22276 cls: 'timepicker-minute',
22291 cls: 'btn btn-primary period',
22313 cls: 'hours-down fa fas fa-chevron-down'
22333 cls: 'minutes-down fa fas fa-chevron-down'
22351 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22358 var hours = this.time.getHours();
22359 var minutes = this.time.getMinutes();
22372 hours = hours - 12;
22376 hours = '0' + hours;
22380 minutes = '0' + minutes;
22383 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22384 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22385 this.pop.select('button', true).first().dom.innerHTML = period;
22391 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22393 var cls = ['bottom'];
22395 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22402 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22406 //this.picker().setXY(20000,20000);
22407 this.picker().addClass(cls.join('-'));
22411 Roo.each(cls, function(c){
22416 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22417 //_this.picker().setTop(_this.inputEl().getHeight());
22421 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22423 //_this.picker().setTop(0 - _this.picker().getHeight());
22428 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22432 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22440 onFocus : function()
22442 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22446 onBlur : function()
22448 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22454 this.picker().show();
22459 this.fireEvent('show', this, this.date);
22464 this.picker().hide();
22467 this.fireEvent('hide', this, this.date);
22470 setTime : function()
22473 this.setValue(this.time.format(this.format));
22475 this.fireEvent('select', this, this.date);
22480 onMousedown: function(e){
22481 e.stopPropagation();
22482 e.preventDefault();
22485 onIncrementHours: function()
22487 Roo.log('onIncrementHours');
22488 this.time = this.time.add(Date.HOUR, 1);
22493 onDecrementHours: function()
22495 Roo.log('onDecrementHours');
22496 this.time = this.time.add(Date.HOUR, -1);
22500 onIncrementMinutes: function()
22502 Roo.log('onIncrementMinutes');
22503 this.time = this.time.add(Date.MINUTE, 1);
22507 onDecrementMinutes: function()
22509 Roo.log('onDecrementMinutes');
22510 this.time = this.time.add(Date.MINUTE, -1);
22514 onTogglePeriod: function()
22516 Roo.log('onTogglePeriod');
22517 this.time = this.time.add(Date.HOUR, 12);
22525 Roo.apply(Roo.bootstrap.TimeField, {
22529 cls: 'datepicker dropdown-menu',
22533 cls: 'datepicker-time',
22537 cls: 'table-condensed',
22566 cls: 'btn btn-info ok',
22594 * @class Roo.bootstrap.MonthField
22595 * @extends Roo.bootstrap.Input
22596 * Bootstrap MonthField class
22598 * @cfg {String} language default en
22601 * Create a new MonthField
22602 * @param {Object} config The config object
22605 Roo.bootstrap.MonthField = function(config){
22606 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22611 * Fires when this field show.
22612 * @param {Roo.bootstrap.MonthField} this
22613 * @param {Mixed} date The date value
22618 * Fires when this field hide.
22619 * @param {Roo.bootstrap.MonthField} this
22620 * @param {Mixed} date The date value
22625 * Fires when select a date.
22626 * @param {Roo.bootstrap.MonthField} this
22627 * @param {String} oldvalue The old value
22628 * @param {String} newvalue The new value
22634 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22636 onRender: function(ct, position)
22639 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22641 this.language = this.language || 'en';
22642 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22643 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22645 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22646 this.isInline = false;
22647 this.isInput = true;
22648 this.component = this.el.select('.add-on', true).first() || false;
22649 this.component = (this.component && this.component.length === 0) ? false : this.component;
22650 this.hasInput = this.component && this.inputEL().length;
22652 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22654 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22656 this.picker().on('mousedown', this.onMousedown, this);
22657 this.picker().on('click', this.onClick, this);
22659 this.picker().addClass('datepicker-dropdown');
22661 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22662 v.setStyle('width', '189px');
22669 if(this.isInline) {
22675 setValue: function(v, suppressEvent)
22677 var o = this.getValue();
22679 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22683 if(suppressEvent !== true){
22684 this.fireEvent('select', this, o, v);
22689 getValue: function()
22694 onClick: function(e)
22696 e.stopPropagation();
22697 e.preventDefault();
22699 var target = e.getTarget();
22701 if(target.nodeName.toLowerCase() === 'i'){
22702 target = Roo.get(target).dom.parentNode;
22705 var nodeName = target.nodeName;
22706 var className = target.className;
22707 var html = target.innerHTML;
22709 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22713 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22715 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22721 picker : function()
22723 return this.pickerEl;
22726 fillMonths: function()
22729 var months = this.picker().select('>.datepicker-months td', true).first();
22731 months.dom.innerHTML = '';
22737 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22740 months.createChild(month);
22749 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22750 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22753 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22754 e.removeClass('active');
22756 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22757 e.addClass('active');
22764 if(this.isInline) {
22768 this.picker().removeClass(['bottom', 'top']);
22770 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22772 * place to the top of element!
22776 this.picker().addClass('top');
22777 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22782 this.picker().addClass('bottom');
22784 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22787 onFocus : function()
22789 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22793 onBlur : function()
22795 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22797 var d = this.inputEl().getValue();
22806 this.picker().show();
22807 this.picker().select('>.datepicker-months', true).first().show();
22811 this.fireEvent('show', this, this.date);
22816 if(this.isInline) {
22819 this.picker().hide();
22820 this.fireEvent('hide', this, this.date);
22824 onMousedown: function(e)
22826 e.stopPropagation();
22827 e.preventDefault();
22832 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22836 fireKey: function(e)
22838 if (!this.picker().isVisible()){
22839 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22850 e.preventDefault();
22854 dir = e.keyCode == 37 ? -1 : 1;
22856 this.vIndex = this.vIndex + dir;
22858 if(this.vIndex < 0){
22862 if(this.vIndex > 11){
22866 if(isNaN(this.vIndex)){
22870 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22876 dir = e.keyCode == 38 ? -1 : 1;
22878 this.vIndex = this.vIndex + dir * 4;
22880 if(this.vIndex < 0){
22884 if(this.vIndex > 11){
22888 if(isNaN(this.vIndex)){
22892 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22897 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22898 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22902 e.preventDefault();
22905 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22906 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22922 this.picker().remove();
22927 Roo.apply(Roo.bootstrap.MonthField, {
22946 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22947 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22952 Roo.apply(Roo.bootstrap.MonthField, {
22956 cls: 'datepicker dropdown-menu roo-dynamic',
22960 cls: 'datepicker-months',
22964 cls: 'table-condensed',
22966 Roo.bootstrap.DateField.content
22986 * @class Roo.bootstrap.CheckBox
22987 * @extends Roo.bootstrap.Input
22988 * Bootstrap CheckBox class
22990 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22991 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22992 * @cfg {String} boxLabel The text that appears beside the checkbox
22993 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22994 * @cfg {Boolean} checked initnal the element
22995 * @cfg {Boolean} inline inline the element (default false)
22996 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22997 * @cfg {String} tooltip label tooltip
23000 * Create a new CheckBox
23001 * @param {Object} config The config object
23004 Roo.bootstrap.CheckBox = function(config){
23005 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23010 * Fires when the element is checked or unchecked.
23011 * @param {Roo.bootstrap.CheckBox} this This input
23012 * @param {Boolean} checked The new checked value
23017 * Fires when the element is click.
23018 * @param {Roo.bootstrap.CheckBox} this This input
23025 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23027 inputType: 'checkbox',
23036 // checkbox success does not make any sense really..
23041 getAutoCreate : function()
23043 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23049 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23052 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23058 type : this.inputType,
23059 value : this.inputValue,
23060 cls : 'roo-' + this.inputType, //'form-box',
23061 placeholder : this.placeholder || ''
23065 if(this.inputType != 'radio'){
23069 cls : 'roo-hidden-value',
23070 value : this.checked ? this.inputValue : this.valueOff
23075 if (this.weight) { // Validity check?
23076 cfg.cls += " " + this.inputType + "-" + this.weight;
23079 if (this.disabled) {
23080 input.disabled=true;
23084 input.checked = this.checked;
23089 input.name = this.name;
23091 if(this.inputType != 'radio'){
23092 hidden.name = this.name;
23093 input.name = '_hidden_' + this.name;
23098 input.cls += ' input-' + this.size;
23103 ['xs','sm','md','lg'].map(function(size){
23104 if (settings[size]) {
23105 cfg.cls += ' col-' + size + '-' + settings[size];
23109 var inputblock = input;
23111 if (this.before || this.after) {
23114 cls : 'input-group',
23119 inputblock.cn.push({
23121 cls : 'input-group-addon',
23126 inputblock.cn.push(input);
23128 if(this.inputType != 'radio'){
23129 inputblock.cn.push(hidden);
23133 inputblock.cn.push({
23135 cls : 'input-group-addon',
23141 var boxLabelCfg = false;
23147 //'for': id, // box label is handled by onclick - so no for...
23149 html: this.boxLabel
23152 boxLabelCfg.tooltip = this.tooltip;
23158 if (align ==='left' && this.fieldLabel.length) {
23159 // Roo.log("left and has label");
23164 cls : 'control-label',
23165 html : this.fieldLabel
23176 cfg.cn[1].cn.push(boxLabelCfg);
23179 if(this.labelWidth > 12){
23180 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23183 if(this.labelWidth < 13 && this.labelmd == 0){
23184 this.labelmd = this.labelWidth;
23187 if(this.labellg > 0){
23188 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23189 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23192 if(this.labelmd > 0){
23193 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23194 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23197 if(this.labelsm > 0){
23198 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23199 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23202 if(this.labelxs > 0){
23203 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23204 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23207 } else if ( this.fieldLabel.length) {
23208 // Roo.log(" label");
23212 tag: this.boxLabel ? 'span' : 'label',
23214 cls: 'control-label box-input-label',
23215 //cls : 'input-group-addon',
23216 html : this.fieldLabel
23223 cfg.cn.push(boxLabelCfg);
23228 // Roo.log(" no label && no align");
23229 cfg.cn = [ inputblock ] ;
23231 cfg.cn.push(boxLabelCfg);
23239 if(this.inputType != 'radio'){
23240 cfg.cn.push(hidden);
23248 * return the real input element.
23250 inputEl: function ()
23252 return this.el.select('input.roo-' + this.inputType,true).first();
23254 hiddenEl: function ()
23256 return this.el.select('input.roo-hidden-value',true).first();
23259 labelEl: function()
23261 return this.el.select('label.control-label',true).first();
23263 /* depricated... */
23267 return this.labelEl();
23270 boxLabelEl: function()
23272 return this.el.select('label.box-label',true).first();
23275 initEvents : function()
23277 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23279 this.inputEl().on('click', this.onClick, this);
23281 if (this.boxLabel) {
23282 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23285 this.startValue = this.getValue();
23288 Roo.bootstrap.CheckBox.register(this);
23292 onClick : function(e)
23294 if(this.fireEvent('click', this, e) !== false){
23295 this.setChecked(!this.checked);
23300 setChecked : function(state,suppressEvent)
23302 this.startValue = this.getValue();
23304 if(this.inputType == 'radio'){
23306 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23307 e.dom.checked = false;
23310 this.inputEl().dom.checked = true;
23312 this.inputEl().dom.value = this.inputValue;
23314 if(suppressEvent !== true){
23315 this.fireEvent('check', this, true);
23323 this.checked = state;
23325 this.inputEl().dom.checked = state;
23328 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23330 if(suppressEvent !== true){
23331 this.fireEvent('check', this, state);
23337 getValue : function()
23339 if(this.inputType == 'radio'){
23340 return this.getGroupValue();
23343 return this.hiddenEl().dom.value;
23347 getGroupValue : function()
23349 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23353 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23356 setValue : function(v,suppressEvent)
23358 if(this.inputType == 'radio'){
23359 this.setGroupValue(v, suppressEvent);
23363 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23368 setGroupValue : function(v, suppressEvent)
23370 this.startValue = this.getValue();
23372 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23373 e.dom.checked = false;
23375 if(e.dom.value == v){
23376 e.dom.checked = true;
23380 if(suppressEvent !== true){
23381 this.fireEvent('check', this, true);
23389 validate : function()
23391 if(this.getVisibilityEl().hasClass('hidden')){
23397 (this.inputType == 'radio' && this.validateRadio()) ||
23398 (this.inputType == 'checkbox' && this.validateCheckbox())
23404 this.markInvalid();
23408 validateRadio : function()
23410 if(this.getVisibilityEl().hasClass('hidden')){
23414 if(this.allowBlank){
23420 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23421 if(!e.dom.checked){
23433 validateCheckbox : function()
23436 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23437 //return (this.getValue() == this.inputValue) ? true : false;
23440 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23448 for(var i in group){
23449 if(group[i].el.isVisible(true)){
23457 for(var i in group){
23462 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23469 * Mark this field as valid
23471 markValid : function()
23475 this.fireEvent('valid', this);
23477 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23480 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23487 if(this.inputType == 'radio'){
23488 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23489 var fg = e.findParent('.form-group', false, true);
23490 if (Roo.bootstrap.version == 3) {
23491 fg.removeClass([_this.invalidClass, _this.validClass]);
23492 fg.addClass(_this.validClass);
23494 fg.removeClass(['is-valid', 'is-invalid']);
23495 fg.addClass('is-valid');
23503 var fg = this.el.findParent('.form-group', false, true);
23504 if (Roo.bootstrap.version == 3) {
23505 fg.removeClass([this.invalidClass, this.validClass]);
23506 fg.addClass(this.validClass);
23508 fg.removeClass(['is-valid', 'is-invalid']);
23509 fg.addClass('is-valid');
23514 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23520 for(var i in group){
23521 var fg = group[i].el.findParent('.form-group', false, true);
23522 if (Roo.bootstrap.version == 3) {
23523 fg.removeClass([this.invalidClass, this.validClass]);
23524 fg.addClass(this.validClass);
23526 fg.removeClass(['is-valid', 'is-invalid']);
23527 fg.addClass('is-valid');
23533 * Mark this field as invalid
23534 * @param {String} msg The validation message
23536 markInvalid : function(msg)
23538 if(this.allowBlank){
23544 this.fireEvent('invalid', this, msg);
23546 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23549 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23553 label.markInvalid();
23556 if(this.inputType == 'radio'){
23558 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23559 var fg = e.findParent('.form-group', false, true);
23560 if (Roo.bootstrap.version == 3) {
23561 fg.removeClass([_this.invalidClass, _this.validClass]);
23562 fg.addClass(_this.invalidClass);
23564 fg.removeClass(['is-invalid', 'is-valid']);
23565 fg.addClass('is-invalid');
23573 var fg = this.el.findParent('.form-group', false, true);
23574 if (Roo.bootstrap.version == 3) {
23575 fg.removeClass([_this.invalidClass, _this.validClass]);
23576 fg.addClass(_this.invalidClass);
23578 fg.removeClass(['is-invalid', 'is-valid']);
23579 fg.addClass('is-invalid');
23584 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23590 for(var i in group){
23591 var fg = group[i].el.findParent('.form-group', false, true);
23592 if (Roo.bootstrap.version == 3) {
23593 fg.removeClass([_this.invalidClass, _this.validClass]);
23594 fg.addClass(_this.invalidClass);
23596 fg.removeClass(['is-invalid', 'is-valid']);
23597 fg.addClass('is-invalid');
23603 clearInvalid : function()
23605 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23607 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23609 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23611 if (label && label.iconEl) {
23612 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23613 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23617 disable : function()
23619 if(this.inputType != 'radio'){
23620 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23627 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23628 _this.getActionEl().addClass(this.disabledClass);
23629 e.dom.disabled = true;
23633 this.disabled = true;
23634 this.fireEvent("disable", this);
23638 enable : function()
23640 if(this.inputType != 'radio'){
23641 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23648 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23649 _this.getActionEl().removeClass(this.disabledClass);
23650 e.dom.disabled = false;
23654 this.disabled = false;
23655 this.fireEvent("enable", this);
23659 setBoxLabel : function(v)
23664 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23670 Roo.apply(Roo.bootstrap.CheckBox, {
23675 * register a CheckBox Group
23676 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23678 register : function(checkbox)
23680 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23681 this.groups[checkbox.groupId] = {};
23684 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23688 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23692 * fetch a CheckBox Group based on the group ID
23693 * @param {string} the group ID
23694 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23696 get: function(groupId) {
23697 if (typeof(this.groups[groupId]) == 'undefined') {
23701 return this.groups[groupId] ;
23714 * @class Roo.bootstrap.Radio
23715 * @extends Roo.bootstrap.Component
23716 * Bootstrap Radio class
23717 * @cfg {String} boxLabel - the label associated
23718 * @cfg {String} value - the value of radio
23721 * Create a new Radio
23722 * @param {Object} config The config object
23724 Roo.bootstrap.Radio = function(config){
23725 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23729 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23735 getAutoCreate : function()
23739 cls : 'form-group radio',
23744 html : this.boxLabel
23752 initEvents : function()
23754 this.parent().register(this);
23756 this.el.on('click', this.onClick, this);
23760 onClick : function(e)
23762 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23763 this.setChecked(true);
23767 setChecked : function(state, suppressEvent)
23769 this.parent().setValue(this.value, suppressEvent);
23773 setBoxLabel : function(v)
23778 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23793 * @class Roo.bootstrap.SecurePass
23794 * @extends Roo.bootstrap.Input
23795 * Bootstrap SecurePass class
23799 * Create a new SecurePass
23800 * @param {Object} config The config object
23803 Roo.bootstrap.SecurePass = function (config) {
23804 // these go here, so the translation tool can replace them..
23806 PwdEmpty: "Please type a password, and then retype it to confirm.",
23807 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23808 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23809 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23810 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23811 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23812 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23813 TooWeak: "Your password is Too Weak."
23815 this.meterLabel = "Password strength:";
23816 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23817 this.meterClass = [
23818 "roo-password-meter-tooweak",
23819 "roo-password-meter-weak",
23820 "roo-password-meter-medium",
23821 "roo-password-meter-strong",
23822 "roo-password-meter-grey"
23827 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23830 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23832 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23834 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23835 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23836 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23837 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23838 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23839 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23840 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23850 * @cfg {String/Object} Label for the strength meter (defaults to
23851 * 'Password strength:')
23856 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23857 * ['Weak', 'Medium', 'Strong'])
23860 pwdStrengths: false,
23873 initEvents: function ()
23875 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23877 if (this.el.is('input[type=password]') && Roo.isSafari) {
23878 this.el.on('keydown', this.SafariOnKeyDown, this);
23881 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23884 onRender: function (ct, position)
23886 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23887 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23888 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23890 this.trigger.createChild({
23895 cls: 'roo-password-meter-grey col-xs-12',
23898 //width: this.meterWidth + 'px'
23902 cls: 'roo-password-meter-text'
23908 if (this.hideTrigger) {
23909 this.trigger.setDisplayed(false);
23911 this.setSize(this.width || '', this.height || '');
23914 onDestroy: function ()
23916 if (this.trigger) {
23917 this.trigger.removeAllListeners();
23918 this.trigger.remove();
23921 this.wrap.remove();
23923 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23926 checkStrength: function ()
23928 var pwd = this.inputEl().getValue();
23929 if (pwd == this._lastPwd) {
23934 if (this.ClientSideStrongPassword(pwd)) {
23936 } else if (this.ClientSideMediumPassword(pwd)) {
23938 } else if (this.ClientSideWeakPassword(pwd)) {
23944 Roo.log('strength1: ' + strength);
23946 //var pm = this.trigger.child('div/div/div').dom;
23947 var pm = this.trigger.child('div/div');
23948 pm.removeClass(this.meterClass);
23949 pm.addClass(this.meterClass[strength]);
23952 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23954 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23956 this._lastPwd = pwd;
23960 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23962 this._lastPwd = '';
23964 var pm = this.trigger.child('div/div');
23965 pm.removeClass(this.meterClass);
23966 pm.addClass('roo-password-meter-grey');
23969 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23972 this.inputEl().dom.type='password';
23975 validateValue: function (value)
23977 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23980 if (value.length == 0) {
23981 if (this.allowBlank) {
23982 this.clearInvalid();
23986 this.markInvalid(this.errors.PwdEmpty);
23987 this.errorMsg = this.errors.PwdEmpty;
23995 if (!value.match(/[\x21-\x7e]+/)) {
23996 this.markInvalid(this.errors.PwdBadChar);
23997 this.errorMsg = this.errors.PwdBadChar;
24000 if (value.length < 6) {
24001 this.markInvalid(this.errors.PwdShort);
24002 this.errorMsg = this.errors.PwdShort;
24005 if (value.length > 16) {
24006 this.markInvalid(this.errors.PwdLong);
24007 this.errorMsg = this.errors.PwdLong;
24011 if (this.ClientSideStrongPassword(value)) {
24013 } else if (this.ClientSideMediumPassword(value)) {
24015 } else if (this.ClientSideWeakPassword(value)) {
24022 if (strength < 2) {
24023 //this.markInvalid(this.errors.TooWeak);
24024 this.errorMsg = this.errors.TooWeak;
24029 console.log('strength2: ' + strength);
24031 //var pm = this.trigger.child('div/div/div').dom;
24033 var pm = this.trigger.child('div/div');
24034 pm.removeClass(this.meterClass);
24035 pm.addClass(this.meterClass[strength]);
24037 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24039 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24041 this.errorMsg = '';
24045 CharacterSetChecks: function (type)
24048 this.fResult = false;
24051 isctype: function (character, type)
24054 case this.kCapitalLetter:
24055 if (character >= 'A' && character <= 'Z') {
24060 case this.kSmallLetter:
24061 if (character >= 'a' && character <= 'z') {
24067 if (character >= '0' && character <= '9') {
24072 case this.kPunctuation:
24073 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24084 IsLongEnough: function (pwd, size)
24086 return !(pwd == null || isNaN(size) || pwd.length < size);
24089 SpansEnoughCharacterSets: function (word, nb)
24091 if (!this.IsLongEnough(word, nb))
24096 var characterSetChecks = new Array(
24097 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24098 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24101 for (var index = 0; index < word.length; ++index) {
24102 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24103 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24104 characterSetChecks[nCharSet].fResult = true;
24111 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24112 if (characterSetChecks[nCharSet].fResult) {
24117 if (nCharSets < nb) {
24123 ClientSideStrongPassword: function (pwd)
24125 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24128 ClientSideMediumPassword: function (pwd)
24130 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24133 ClientSideWeakPassword: function (pwd)
24135 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24138 })//<script type="text/javascript">
24141 * Based Ext JS Library 1.1.1
24142 * Copyright(c) 2006-2007, Ext JS, LLC.
24148 * @class Roo.HtmlEditorCore
24149 * @extends Roo.Component
24150 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24152 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24155 Roo.HtmlEditorCore = function(config){
24158 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24163 * @event initialize
24164 * Fires when the editor is fully initialized (including the iframe)
24165 * @param {Roo.HtmlEditorCore} this
24170 * Fires when the editor is first receives the focus. Any insertion must wait
24171 * until after this event.
24172 * @param {Roo.HtmlEditorCore} this
24176 * @event beforesync
24177 * Fires before the textarea is updated with content from the editor iframe. Return false
24178 * to cancel the sync.
24179 * @param {Roo.HtmlEditorCore} this
24180 * @param {String} html
24184 * @event beforepush
24185 * Fires before the iframe editor is updated with content from the textarea. Return false
24186 * to cancel the push.
24187 * @param {Roo.HtmlEditorCore} this
24188 * @param {String} html
24193 * Fires when the textarea is updated with content from the editor iframe.
24194 * @param {Roo.HtmlEditorCore} this
24195 * @param {String} html
24200 * Fires when the iframe editor is updated with content from the textarea.
24201 * @param {Roo.HtmlEditorCore} this
24202 * @param {String} html
24207 * @event editorevent
24208 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24209 * @param {Roo.HtmlEditorCore} this
24215 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24217 // defaults : white / black...
24218 this.applyBlacklists();
24225 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24229 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24235 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24240 * @cfg {Number} height (in pixels)
24244 * @cfg {Number} width (in pixels)
24249 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24252 stylesheets: false,
24257 // private properties
24258 validationEvent : false,
24260 initialized : false,
24262 sourceEditMode : false,
24263 onFocus : Roo.emptyFn,
24265 hideMode:'offsets',
24269 // blacklist + whitelisted elements..
24276 * Protected method that will not generally be called directly. It
24277 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24278 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24280 getDocMarkup : function(){
24284 // inherit styels from page...??
24285 if (this.stylesheets === false) {
24287 Roo.get(document.head).select('style').each(function(node) {
24288 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24291 Roo.get(document.head).select('link').each(function(node) {
24292 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24295 } else if (!this.stylesheets.length) {
24297 st = '<style type="text/css">' +
24298 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24301 for (var i in this.stylesheets) {
24302 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24307 st += '<style type="text/css">' +
24308 'IMG { cursor: pointer } ' +
24311 var cls = 'roo-htmleditor-body';
24313 if(this.bodyCls.length){
24314 cls += ' ' + this.bodyCls;
24317 return '<html><head>' + st +
24318 //<style type="text/css">' +
24319 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24321 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24325 onRender : function(ct, position)
24328 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24329 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24332 this.el.dom.style.border = '0 none';
24333 this.el.dom.setAttribute('tabIndex', -1);
24334 this.el.addClass('x-hidden hide');
24338 if(Roo.isIE){ // fix IE 1px bogus margin
24339 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24343 this.frameId = Roo.id();
24347 var iframe = this.owner.wrap.createChild({
24349 cls: 'form-control', // bootstrap..
24351 name: this.frameId,
24352 frameBorder : 'no',
24353 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24358 this.iframe = iframe.dom;
24360 this.assignDocWin();
24362 this.doc.designMode = 'on';
24365 this.doc.write(this.getDocMarkup());
24369 var task = { // must defer to wait for browser to be ready
24371 //console.log("run task?" + this.doc.readyState);
24372 this.assignDocWin();
24373 if(this.doc.body || this.doc.readyState == 'complete'){
24375 this.doc.designMode="on";
24379 Roo.TaskMgr.stop(task);
24380 this.initEditor.defer(10, this);
24387 Roo.TaskMgr.start(task);
24392 onResize : function(w, h)
24394 Roo.log('resize: ' +w + ',' + h );
24395 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24399 if(typeof w == 'number'){
24401 this.iframe.style.width = w + 'px';
24403 if(typeof h == 'number'){
24405 this.iframe.style.height = h + 'px';
24407 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24414 * Toggles the editor between standard and source edit mode.
24415 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24417 toggleSourceEdit : function(sourceEditMode){
24419 this.sourceEditMode = sourceEditMode === true;
24421 if(this.sourceEditMode){
24423 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24426 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24427 //this.iframe.className = '';
24430 //this.setSize(this.owner.wrap.getSize());
24431 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24438 * Protected method that will not generally be called directly. If you need/want
24439 * custom HTML cleanup, this is the method you should override.
24440 * @param {String} html The HTML to be cleaned
24441 * return {String} The cleaned HTML
24443 cleanHtml : function(html){
24444 html = String(html);
24445 if(html.length > 5){
24446 if(Roo.isSafari){ // strip safari nonsense
24447 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24450 if(html == ' '){
24457 * HTML Editor -> Textarea
24458 * Protected method that will not generally be called directly. Syncs the contents
24459 * of the editor iframe with the textarea.
24461 syncValue : function(){
24462 if(this.initialized){
24463 var bd = (this.doc.body || this.doc.documentElement);
24464 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24465 var html = bd.innerHTML;
24467 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24468 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24470 html = '<div style="'+m[0]+'">' + html + '</div>';
24473 html = this.cleanHtml(html);
24474 // fix up the special chars.. normaly like back quotes in word...
24475 // however we do not want to do this with chinese..
24476 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24478 var cc = match.charCodeAt();
24480 // Get the character value, handling surrogate pairs
24481 if (match.length == 2) {
24482 // It's a surrogate pair, calculate the Unicode code point
24483 var high = match.charCodeAt(0) - 0xD800;
24484 var low = match.charCodeAt(1) - 0xDC00;
24485 cc = (high * 0x400) + low + 0x10000;
24487 (cc >= 0x4E00 && cc < 0xA000 ) ||
24488 (cc >= 0x3400 && cc < 0x4E00 ) ||
24489 (cc >= 0xf900 && cc < 0xfb00 )
24494 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24495 return "&#" + cc + ";";
24502 if(this.owner.fireEvent('beforesync', this, html) !== false){
24503 this.el.dom.value = html;
24504 this.owner.fireEvent('sync', this, html);
24510 * Protected method that will not generally be called directly. Pushes the value of the textarea
24511 * into the iframe editor.
24513 pushValue : function(){
24514 if(this.initialized){
24515 var v = this.el.dom.value.trim();
24517 // if(v.length < 1){
24521 if(this.owner.fireEvent('beforepush', this, v) !== false){
24522 var d = (this.doc.body || this.doc.documentElement);
24524 this.cleanUpPaste();
24525 this.el.dom.value = d.innerHTML;
24526 this.owner.fireEvent('push', this, v);
24532 deferFocus : function(){
24533 this.focus.defer(10, this);
24537 focus : function(){
24538 if(this.win && !this.sourceEditMode){
24545 assignDocWin: function()
24547 var iframe = this.iframe;
24550 this.doc = iframe.contentWindow.document;
24551 this.win = iframe.contentWindow;
24553 // if (!Roo.get(this.frameId)) {
24556 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24557 // this.win = Roo.get(this.frameId).dom.contentWindow;
24559 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24563 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24564 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24569 initEditor : function(){
24570 //console.log("INIT EDITOR");
24571 this.assignDocWin();
24575 this.doc.designMode="on";
24577 this.doc.write(this.getDocMarkup());
24580 var dbody = (this.doc.body || this.doc.documentElement);
24581 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24582 // this copies styles from the containing element into thsi one..
24583 // not sure why we need all of this..
24584 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24586 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24587 //ss['background-attachment'] = 'fixed'; // w3c
24588 dbody.bgProperties = 'fixed'; // ie
24589 //Roo.DomHelper.applyStyles(dbody, ss);
24590 Roo.EventManager.on(this.doc, {
24591 //'mousedown': this.onEditorEvent,
24592 'mouseup': this.onEditorEvent,
24593 'dblclick': this.onEditorEvent,
24594 'click': this.onEditorEvent,
24595 'keyup': this.onEditorEvent,
24600 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24602 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24603 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24605 this.initialized = true;
24607 this.owner.fireEvent('initialize', this);
24612 onDestroy : function(){
24618 //for (var i =0; i < this.toolbars.length;i++) {
24619 // // fixme - ask toolbars for heights?
24620 // this.toolbars[i].onDestroy();
24623 //this.wrap.dom.innerHTML = '';
24624 //this.wrap.remove();
24629 onFirstFocus : function(){
24631 this.assignDocWin();
24634 this.activated = true;
24637 if(Roo.isGecko){ // prevent silly gecko errors
24639 var s = this.win.getSelection();
24640 if(!s.focusNode || s.focusNode.nodeType != 3){
24641 var r = s.getRangeAt(0);
24642 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24647 this.execCmd('useCSS', true);
24648 this.execCmd('styleWithCSS', false);
24651 this.owner.fireEvent('activate', this);
24655 adjustFont: function(btn){
24656 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24657 //if(Roo.isSafari){ // safari
24660 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24661 if(Roo.isSafari){ // safari
24662 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24663 v = (v < 10) ? 10 : v;
24664 v = (v > 48) ? 48 : v;
24665 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24670 v = Math.max(1, v+adjust);
24672 this.execCmd('FontSize', v );
24675 onEditorEvent : function(e)
24677 this.owner.fireEvent('editorevent', this, e);
24678 // this.updateToolbar();
24679 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24682 insertTag : function(tg)
24684 // could be a bit smarter... -> wrap the current selected tRoo..
24685 if (tg.toLowerCase() == 'span' ||
24686 tg.toLowerCase() == 'code' ||
24687 tg.toLowerCase() == 'sup' ||
24688 tg.toLowerCase() == 'sub'
24691 range = this.createRange(this.getSelection());
24692 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24693 wrappingNode.appendChild(range.extractContents());
24694 range.insertNode(wrappingNode);
24701 this.execCmd("formatblock", tg);
24705 insertText : function(txt)
24709 var range = this.createRange();
24710 range.deleteContents();
24711 //alert(Sender.getAttribute('label'));
24713 range.insertNode(this.doc.createTextNode(txt));
24719 * Executes a Midas editor command on the editor document and performs necessary focus and
24720 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24721 * @param {String} cmd The Midas command
24722 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24724 relayCmd : function(cmd, value){
24726 this.execCmd(cmd, value);
24727 this.owner.fireEvent('editorevent', this);
24728 //this.updateToolbar();
24729 this.owner.deferFocus();
24733 * Executes a Midas editor command directly on the editor document.
24734 * For visual commands, you should use {@link #relayCmd} instead.
24735 * <b>This should only be called after the editor is initialized.</b>
24736 * @param {String} cmd The Midas command
24737 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24739 execCmd : function(cmd, value){
24740 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24747 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24749 * @param {String} text | dom node..
24751 insertAtCursor : function(text)
24754 if(!this.activated){
24760 var r = this.doc.selection.createRange();
24771 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24775 // from jquery ui (MIT licenced)
24777 var win = this.win;
24779 if (win.getSelection && win.getSelection().getRangeAt) {
24780 range = win.getSelection().getRangeAt(0);
24781 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24782 range.insertNode(node);
24783 } else if (win.document.selection && win.document.selection.createRange) {
24784 // no firefox support
24785 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24786 win.document.selection.createRange().pasteHTML(txt);
24788 // no firefox support
24789 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24790 this.execCmd('InsertHTML', txt);
24799 mozKeyPress : function(e){
24801 var c = e.getCharCode(), cmd;
24804 c = String.fromCharCode(c).toLowerCase();
24818 this.cleanUpPaste.defer(100, this);
24826 e.preventDefault();
24834 fixKeys : function(){ // load time branching for fastest keydown performance
24836 return function(e){
24837 var k = e.getKey(), r;
24840 r = this.doc.selection.createRange();
24843 r.pasteHTML('    ');
24850 r = this.doc.selection.createRange();
24852 var target = r.parentElement();
24853 if(!target || target.tagName.toLowerCase() != 'li'){
24855 r.pasteHTML('<br />');
24861 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24862 this.cleanUpPaste.defer(100, this);
24868 }else if(Roo.isOpera){
24869 return function(e){
24870 var k = e.getKey();
24874 this.execCmd('InsertHTML','    ');
24877 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24878 this.cleanUpPaste.defer(100, this);
24883 }else if(Roo.isSafari){
24884 return function(e){
24885 var k = e.getKey();
24889 this.execCmd('InsertText','\t');
24893 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24894 this.cleanUpPaste.defer(100, this);
24902 getAllAncestors: function()
24904 var p = this.getSelectedNode();
24907 a.push(p); // push blank onto stack..
24908 p = this.getParentElement();
24912 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24916 a.push(this.doc.body);
24920 lastSelNode : false,
24923 getSelection : function()
24925 this.assignDocWin();
24926 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24929 getSelectedNode: function()
24931 // this may only work on Gecko!!!
24933 // should we cache this!!!!
24938 var range = this.createRange(this.getSelection()).cloneRange();
24941 var parent = range.parentElement();
24943 var testRange = range.duplicate();
24944 testRange.moveToElementText(parent);
24945 if (testRange.inRange(range)) {
24948 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24951 parent = parent.parentElement;
24956 // is ancestor a text element.
24957 var ac = range.commonAncestorContainer;
24958 if (ac.nodeType == 3) {
24959 ac = ac.parentNode;
24962 var ar = ac.childNodes;
24965 var other_nodes = [];
24966 var has_other_nodes = false;
24967 for (var i=0;i<ar.length;i++) {
24968 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24971 // fullly contained node.
24973 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24978 // probably selected..
24979 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24980 other_nodes.push(ar[i]);
24984 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24989 has_other_nodes = true;
24991 if (!nodes.length && other_nodes.length) {
24992 nodes= other_nodes;
24994 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25000 createRange: function(sel)
25002 // this has strange effects when using with
25003 // top toolbar - not sure if it's a great idea.
25004 //this.editor.contentWindow.focus();
25005 if (typeof sel != "undefined") {
25007 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25009 return this.doc.createRange();
25012 return this.doc.createRange();
25015 getParentElement: function()
25018 this.assignDocWin();
25019 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25021 var range = this.createRange(sel);
25024 var p = range.commonAncestorContainer;
25025 while (p.nodeType == 3) { // text node
25036 * Range intersection.. the hard stuff...
25040 * [ -- selected range --- ]
25044 * if end is before start or hits it. fail.
25045 * if start is after end or hits it fail.
25047 * if either hits (but other is outside. - then it's not
25053 // @see http://www.thismuchiknow.co.uk/?p=64.
25054 rangeIntersectsNode : function(range, node)
25056 var nodeRange = node.ownerDocument.createRange();
25058 nodeRange.selectNode(node);
25060 nodeRange.selectNodeContents(node);
25063 var rangeStartRange = range.cloneRange();
25064 rangeStartRange.collapse(true);
25066 var rangeEndRange = range.cloneRange();
25067 rangeEndRange.collapse(false);
25069 var nodeStartRange = nodeRange.cloneRange();
25070 nodeStartRange.collapse(true);
25072 var nodeEndRange = nodeRange.cloneRange();
25073 nodeEndRange.collapse(false);
25075 return rangeStartRange.compareBoundaryPoints(
25076 Range.START_TO_START, nodeEndRange) == -1 &&
25077 rangeEndRange.compareBoundaryPoints(
25078 Range.START_TO_START, nodeStartRange) == 1;
25082 rangeCompareNode : function(range, node)
25084 var nodeRange = node.ownerDocument.createRange();
25086 nodeRange.selectNode(node);
25088 nodeRange.selectNodeContents(node);
25092 range.collapse(true);
25094 nodeRange.collapse(true);
25096 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25097 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25099 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25101 var nodeIsBefore = ss == 1;
25102 var nodeIsAfter = ee == -1;
25104 if (nodeIsBefore && nodeIsAfter) {
25107 if (!nodeIsBefore && nodeIsAfter) {
25108 return 1; //right trailed.
25111 if (nodeIsBefore && !nodeIsAfter) {
25112 return 2; // left trailed.
25118 // private? - in a new class?
25119 cleanUpPaste : function()
25121 // cleans up the whole document..
25122 Roo.log('cleanuppaste');
25124 this.cleanUpChildren(this.doc.body);
25125 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25126 if (clean != this.doc.body.innerHTML) {
25127 this.doc.body.innerHTML = clean;
25132 cleanWordChars : function(input) {// change the chars to hex code
25133 var he = Roo.HtmlEditorCore;
25135 var output = input;
25136 Roo.each(he.swapCodes, function(sw) {
25137 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25139 output = output.replace(swapper, sw[1]);
25146 cleanUpChildren : function (n)
25148 if (!n.childNodes.length) {
25151 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25152 this.cleanUpChild(n.childNodes[i]);
25159 cleanUpChild : function (node)
25162 //console.log(node);
25163 if (node.nodeName == "#text") {
25164 // clean up silly Windows -- stuff?
25167 if (node.nodeName == "#comment") {
25168 node.parentNode.removeChild(node);
25169 // clean up silly Windows -- stuff?
25172 var lcname = node.tagName.toLowerCase();
25173 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25174 // whitelist of tags..
25176 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25178 node.parentNode.removeChild(node);
25183 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25185 // spans with no attributes - just remove them..
25186 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25187 remove_keep_children = true;
25190 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25191 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25193 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25194 // remove_keep_children = true;
25197 if (remove_keep_children) {
25198 this.cleanUpChildren(node);
25199 // inserts everything just before this node...
25200 while (node.childNodes.length) {
25201 var cn = node.childNodes[0];
25202 node.removeChild(cn);
25203 node.parentNode.insertBefore(cn, node);
25205 node.parentNode.removeChild(node);
25209 if (!node.attributes || !node.attributes.length) {
25214 this.cleanUpChildren(node);
25218 function cleanAttr(n,v)
25221 if (v.match(/^\./) || v.match(/^\//)) {
25224 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25227 if (v.match(/^#/)) {
25230 if (v.match(/^\{/)) { // allow template editing.
25233 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25234 node.removeAttribute(n);
25238 var cwhite = this.cwhite;
25239 var cblack = this.cblack;
25241 function cleanStyle(n,v)
25243 if (v.match(/expression/)) { //XSS?? should we even bother..
25244 node.removeAttribute(n);
25248 var parts = v.split(/;/);
25251 Roo.each(parts, function(p) {
25252 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25256 var l = p.split(':').shift().replace(/\s+/g,'');
25257 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25259 if ( cwhite.length && cblack.indexOf(l) > -1) {
25260 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25261 //node.removeAttribute(n);
25265 // only allow 'c whitelisted system attributes'
25266 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25267 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25268 //node.removeAttribute(n);
25278 if (clean.length) {
25279 node.setAttribute(n, clean.join(';'));
25281 node.removeAttribute(n);
25287 for (var i = node.attributes.length-1; i > -1 ; i--) {
25288 var a = node.attributes[i];
25291 if (a.name.toLowerCase().substr(0,2)=='on') {
25292 node.removeAttribute(a.name);
25295 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25296 node.removeAttribute(a.name);
25299 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25300 cleanAttr(a.name,a.value); // fixme..
25303 if (a.name == 'style') {
25304 cleanStyle(a.name,a.value);
25307 /// clean up MS crap..
25308 // tecnically this should be a list of valid class'es..
25311 if (a.name == 'class') {
25312 if (a.value.match(/^Mso/)) {
25313 node.removeAttribute('class');
25316 if (a.value.match(/^body$/)) {
25317 node.removeAttribute('class');
25328 this.cleanUpChildren(node);
25334 * Clean up MS wordisms...
25336 cleanWord : function(node)
25339 this.cleanWord(this.doc.body);
25344 node.nodeName == 'SPAN' &&
25345 !node.hasAttributes() &&
25346 node.childNodes.length == 1 &&
25347 node.firstChild.nodeName == "#text"
25349 var textNode = node.firstChild;
25350 node.removeChild(textNode);
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.insertBefore(textNode, node);
25355 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25356 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25358 node.parentNode.removeChild(node);
25361 if (node.nodeName == "#text") {
25362 // clean up silly Windows -- stuff?
25365 if (node.nodeName == "#comment") {
25366 node.parentNode.removeChild(node);
25367 // clean up silly Windows -- stuff?
25371 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25372 node.parentNode.removeChild(node);
25375 //Roo.log(node.tagName);
25376 // remove - but keep children..
25377 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25378 //Roo.log('-- removed');
25379 while (node.childNodes.length) {
25380 var cn = node.childNodes[0];
25381 node.removeChild(cn);
25382 node.parentNode.insertBefore(cn, node);
25383 // move node to parent - and clean it..
25384 this.cleanWord(cn);
25386 node.parentNode.removeChild(node);
25387 /// no need to iterate chidlren = it's got none..
25388 //this.iterateChildren(node, this.cleanWord);
25392 if (node.className.length) {
25394 var cn = node.className.split(/\W+/);
25396 Roo.each(cn, function(cls) {
25397 if (cls.match(/Mso[a-zA-Z]+/)) {
25402 node.className = cna.length ? cna.join(' ') : '';
25404 node.removeAttribute("class");
25408 if (node.hasAttribute("lang")) {
25409 node.removeAttribute("lang");
25412 if (node.hasAttribute("style")) {
25414 var styles = node.getAttribute("style").split(";");
25416 Roo.each(styles, function(s) {
25417 if (!s.match(/:/)) {
25420 var kv = s.split(":");
25421 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25424 // what ever is left... we allow.
25427 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25428 if (!nstyle.length) {
25429 node.removeAttribute('style');
25432 this.iterateChildren(node, this.cleanWord);
25438 * iterateChildren of a Node, calling fn each time, using this as the scole..
25439 * @param {DomNode} node node to iterate children of.
25440 * @param {Function} fn method of this class to call on each item.
25442 iterateChildren : function(node, fn)
25444 if (!node.childNodes.length) {
25447 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25448 fn.call(this, node.childNodes[i])
25454 * cleanTableWidths.
25456 * Quite often pasting from word etc.. results in tables with column and widths.
25457 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25460 cleanTableWidths : function(node)
25465 this.cleanTableWidths(this.doc.body);
25470 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25473 Roo.log(node.tagName);
25474 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25475 this.iterateChildren(node, this.cleanTableWidths);
25478 if (node.hasAttribute('width')) {
25479 node.removeAttribute('width');
25483 if (node.hasAttribute("style")) {
25486 var styles = node.getAttribute("style").split(";");
25488 Roo.each(styles, function(s) {
25489 if (!s.match(/:/)) {
25492 var kv = s.split(":");
25493 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25496 // what ever is left... we allow.
25499 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25500 if (!nstyle.length) {
25501 node.removeAttribute('style');
25505 this.iterateChildren(node, this.cleanTableWidths);
25513 domToHTML : function(currentElement, depth, nopadtext) {
25515 depth = depth || 0;
25516 nopadtext = nopadtext || false;
25518 if (!currentElement) {
25519 return this.domToHTML(this.doc.body);
25522 //Roo.log(currentElement);
25524 var allText = false;
25525 var nodeName = currentElement.nodeName;
25526 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25528 if (nodeName == '#text') {
25530 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25535 if (nodeName != 'BODY') {
25538 // Prints the node tagName, such as <A>, <IMG>, etc
25541 for(i = 0; i < currentElement.attributes.length;i++) {
25543 var aname = currentElement.attributes.item(i).name;
25544 if (!currentElement.attributes.item(i).value.length) {
25547 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25550 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25559 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25562 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25567 // Traverse the tree
25569 var currentElementChild = currentElement.childNodes.item(i);
25570 var allText = true;
25571 var innerHTML = '';
25573 while (currentElementChild) {
25574 // Formatting code (indent the tree so it looks nice on the screen)
25575 var nopad = nopadtext;
25576 if (lastnode == 'SPAN') {
25580 if (currentElementChild.nodeName == '#text') {
25581 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25582 toadd = nopadtext ? toadd : toadd.trim();
25583 if (!nopad && toadd.length > 80) {
25584 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25586 innerHTML += toadd;
25589 currentElementChild = currentElement.childNodes.item(i);
25595 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25597 // Recursively traverse the tree structure of the child node
25598 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25599 lastnode = currentElementChild.nodeName;
25601 currentElementChild=currentElement.childNodes.item(i);
25607 // The remaining code is mostly for formatting the tree
25608 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25613 ret+= "</"+tagName+">";
25619 applyBlacklists : function()
25621 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25622 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25626 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25627 if (b.indexOf(tag) > -1) {
25630 this.white.push(tag);
25634 Roo.each(w, function(tag) {
25635 if (b.indexOf(tag) > -1) {
25638 if (this.white.indexOf(tag) > -1) {
25641 this.white.push(tag);
25646 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25647 if (w.indexOf(tag) > -1) {
25650 this.black.push(tag);
25654 Roo.each(b, function(tag) {
25655 if (w.indexOf(tag) > -1) {
25658 if (this.black.indexOf(tag) > -1) {
25661 this.black.push(tag);
25666 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25667 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25671 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25672 if (b.indexOf(tag) > -1) {
25675 this.cwhite.push(tag);
25679 Roo.each(w, function(tag) {
25680 if (b.indexOf(tag) > -1) {
25683 if (this.cwhite.indexOf(tag) > -1) {
25686 this.cwhite.push(tag);
25691 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25692 if (w.indexOf(tag) > -1) {
25695 this.cblack.push(tag);
25699 Roo.each(b, function(tag) {
25700 if (w.indexOf(tag) > -1) {
25703 if (this.cblack.indexOf(tag) > -1) {
25706 this.cblack.push(tag);
25711 setStylesheets : function(stylesheets)
25713 if(typeof(stylesheets) == 'string'){
25714 Roo.get(this.iframe.contentDocument.head).createChild({
25716 rel : 'stylesheet',
25725 Roo.each(stylesheets, function(s) {
25730 Roo.get(_this.iframe.contentDocument.head).createChild({
25732 rel : 'stylesheet',
25741 removeStylesheets : function()
25745 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25750 setStyle : function(style)
25752 Roo.get(this.iframe.contentDocument.head).createChild({
25761 // hide stuff that is not compatible
25775 * @event specialkey
25779 * @cfg {String} fieldClass @hide
25782 * @cfg {String} focusClass @hide
25785 * @cfg {String} autoCreate @hide
25788 * @cfg {String} inputType @hide
25791 * @cfg {String} invalidClass @hide
25794 * @cfg {String} invalidText @hide
25797 * @cfg {String} msgFx @hide
25800 * @cfg {String} validateOnBlur @hide
25804 Roo.HtmlEditorCore.white = [
25805 'area', 'br', 'img', 'input', 'hr', 'wbr',
25807 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25808 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25809 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25810 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25811 'table', 'ul', 'xmp',
25813 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25816 'dir', 'menu', 'ol', 'ul', 'dl',
25822 Roo.HtmlEditorCore.black = [
25823 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25825 'base', 'basefont', 'bgsound', 'blink', 'body',
25826 'frame', 'frameset', 'head', 'html', 'ilayer',
25827 'iframe', 'layer', 'link', 'meta', 'object',
25828 'script', 'style' ,'title', 'xml' // clean later..
25830 Roo.HtmlEditorCore.clean = [
25831 'script', 'style', 'title', 'xml'
25833 Roo.HtmlEditorCore.remove = [
25838 Roo.HtmlEditorCore.ablack = [
25842 Roo.HtmlEditorCore.aclean = [
25843 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25847 Roo.HtmlEditorCore.pwhite= [
25848 'http', 'https', 'mailto'
25851 // white listed style attributes.
25852 Roo.HtmlEditorCore.cwhite= [
25853 // 'text-align', /// default is to allow most things..
25859 // black listed style attributes.
25860 Roo.HtmlEditorCore.cblack= [
25861 // 'font-size' -- this can be set by the project
25865 Roo.HtmlEditorCore.swapCodes =[
25866 [ 8211, "–" ],
25867 [ 8212, "—" ],
25884 * @class Roo.bootstrap.HtmlEditor
25885 * @extends Roo.bootstrap.TextArea
25886 * Bootstrap HtmlEditor class
25889 * Create a new HtmlEditor
25890 * @param {Object} config The config object
25893 Roo.bootstrap.HtmlEditor = function(config){
25894 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25895 if (!this.toolbars) {
25896 this.toolbars = [];
25899 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25902 * @event initialize
25903 * Fires when the editor is fully initialized (including the iframe)
25904 * @param {HtmlEditor} this
25909 * Fires when the editor is first receives the focus. Any insertion must wait
25910 * until after this event.
25911 * @param {HtmlEditor} this
25915 * @event beforesync
25916 * Fires before the textarea is updated with content from the editor iframe. Return false
25917 * to cancel the sync.
25918 * @param {HtmlEditor} this
25919 * @param {String} html
25923 * @event beforepush
25924 * Fires before the iframe editor is updated with content from the textarea. Return false
25925 * to cancel the push.
25926 * @param {HtmlEditor} this
25927 * @param {String} html
25932 * Fires when the textarea is updated with content from the editor iframe.
25933 * @param {HtmlEditor} this
25934 * @param {String} html
25939 * Fires when the iframe editor is updated with content from the textarea.
25940 * @param {HtmlEditor} this
25941 * @param {String} html
25945 * @event editmodechange
25946 * Fires when the editor switches edit modes
25947 * @param {HtmlEditor} this
25948 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25950 editmodechange: true,
25952 * @event editorevent
25953 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25954 * @param {HtmlEditor} this
25958 * @event firstfocus
25959 * Fires when on first focus - needed by toolbars..
25960 * @param {HtmlEditor} this
25965 * Auto save the htmlEditor value as a file into Events
25966 * @param {HtmlEditor} this
25970 * @event savedpreview
25971 * preview the saved version of htmlEditor
25972 * @param {HtmlEditor} this
25979 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25983 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25988 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25993 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25998 * @cfg {Number} height (in pixels)
26002 * @cfg {Number} width (in pixels)
26007 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26010 stylesheets: false,
26015 // private properties
26016 validationEvent : false,
26018 initialized : false,
26021 onFocus : Roo.emptyFn,
26023 hideMode:'offsets',
26025 tbContainer : false,
26029 toolbarContainer :function() {
26030 return this.wrap.select('.x-html-editor-tb',true).first();
26034 * Protected method that will not generally be called directly. It
26035 * is called when the editor creates its toolbar. Override this method if you need to
26036 * add custom toolbar buttons.
26037 * @param {HtmlEditor} editor
26039 createToolbar : function(){
26040 Roo.log('renewing');
26041 Roo.log("create toolbars");
26043 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26044 this.toolbars[0].render(this.toolbarContainer());
26048 // if (!editor.toolbars || !editor.toolbars.length) {
26049 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26052 // for (var i =0 ; i < editor.toolbars.length;i++) {
26053 // editor.toolbars[i] = Roo.factory(
26054 // typeof(editor.toolbars[i]) == 'string' ?
26055 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26056 // Roo.bootstrap.HtmlEditor);
26057 // editor.toolbars[i].init(editor);
26063 onRender : function(ct, position)
26065 // Roo.log("Call onRender: " + this.xtype);
26067 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26069 this.wrap = this.inputEl().wrap({
26070 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26073 this.editorcore.onRender(ct, position);
26075 if (this.resizable) {
26076 this.resizeEl = new Roo.Resizable(this.wrap, {
26080 minHeight : this.height,
26081 height: this.height,
26082 handles : this.resizable,
26085 resize : function(r, w, h) {
26086 _t.onResize(w,h); // -something
26092 this.createToolbar(this);
26095 if(!this.width && this.resizable){
26096 this.setSize(this.wrap.getSize());
26098 if (this.resizeEl) {
26099 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26100 // should trigger onReize..
26106 onResize : function(w, h)
26108 Roo.log('resize: ' +w + ',' + h );
26109 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26113 if(this.inputEl() ){
26114 if(typeof w == 'number'){
26115 var aw = w - this.wrap.getFrameWidth('lr');
26116 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26119 if(typeof h == 'number'){
26120 var tbh = -11; // fixme it needs to tool bar size!
26121 for (var i =0; i < this.toolbars.length;i++) {
26122 // fixme - ask toolbars for heights?
26123 tbh += this.toolbars[i].el.getHeight();
26124 //if (this.toolbars[i].footer) {
26125 // tbh += this.toolbars[i].footer.el.getHeight();
26133 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26134 ah -= 5; // knock a few pixes off for look..
26135 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26139 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26140 this.editorcore.onResize(ew,eh);
26145 * Toggles the editor between standard and source edit mode.
26146 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26148 toggleSourceEdit : function(sourceEditMode)
26150 this.editorcore.toggleSourceEdit(sourceEditMode);
26152 if(this.editorcore.sourceEditMode){
26153 Roo.log('editor - showing textarea');
26156 // Roo.log(this.syncValue());
26158 this.inputEl().removeClass(['hide', 'x-hidden']);
26159 this.inputEl().dom.removeAttribute('tabIndex');
26160 this.inputEl().focus();
26162 Roo.log('editor - hiding textarea');
26164 // Roo.log(this.pushValue());
26167 this.inputEl().addClass(['hide', 'x-hidden']);
26168 this.inputEl().dom.setAttribute('tabIndex', -1);
26169 //this.deferFocus();
26172 if(this.resizable){
26173 this.setSize(this.wrap.getSize());
26176 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26179 // private (for BoxComponent)
26180 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26182 // private (for BoxComponent)
26183 getResizeEl : function(){
26187 // private (for BoxComponent)
26188 getPositionEl : function(){
26193 initEvents : function(){
26194 this.originalValue = this.getValue();
26198 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26201 // markInvalid : Roo.emptyFn,
26203 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26206 // clearInvalid : Roo.emptyFn,
26208 setValue : function(v){
26209 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26210 this.editorcore.pushValue();
26215 deferFocus : function(){
26216 this.focus.defer(10, this);
26220 focus : function(){
26221 this.editorcore.focus();
26227 onDestroy : function(){
26233 for (var i =0; i < this.toolbars.length;i++) {
26234 // fixme - ask toolbars for heights?
26235 this.toolbars[i].onDestroy();
26238 this.wrap.dom.innerHTML = '';
26239 this.wrap.remove();
26244 onFirstFocus : function(){
26245 //Roo.log("onFirstFocus");
26246 this.editorcore.onFirstFocus();
26247 for (var i =0; i < this.toolbars.length;i++) {
26248 this.toolbars[i].onFirstFocus();
26254 syncValue : function()
26256 this.editorcore.syncValue();
26259 pushValue : function()
26261 this.editorcore.pushValue();
26265 // hide stuff that is not compatible
26279 * @event specialkey
26283 * @cfg {String} fieldClass @hide
26286 * @cfg {String} focusClass @hide
26289 * @cfg {String} autoCreate @hide
26292 * @cfg {String} inputType @hide
26296 * @cfg {String} invalidText @hide
26299 * @cfg {String} msgFx @hide
26302 * @cfg {String} validateOnBlur @hide
26311 Roo.namespace('Roo.bootstrap.htmleditor');
26313 * @class Roo.bootstrap.HtmlEditorToolbar1
26319 new Roo.bootstrap.HtmlEditor({
26322 new Roo.bootstrap.HtmlEditorToolbar1({
26323 disable : { fonts: 1 , format: 1, ..., ... , ...],
26329 * @cfg {Object} disable List of elements to disable..
26330 * @cfg {Array} btns List of additional buttons.
26334 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26337 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26340 Roo.apply(this, config);
26342 // default disabled, based on 'good practice'..
26343 this.disable = this.disable || {};
26344 Roo.applyIf(this.disable, {
26347 specialElements : true
26349 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26351 this.editor = config.editor;
26352 this.editorcore = config.editor.editorcore;
26354 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26356 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26357 // dont call parent... till later.
26359 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26364 editorcore : false,
26369 "h1","h2","h3","h4","h5","h6",
26371 "abbr", "acronym", "address", "cite", "samp", "var",
26375 onRender : function(ct, position)
26377 // Roo.log("Call onRender: " + this.xtype);
26379 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26381 this.el.dom.style.marginBottom = '0';
26383 var editorcore = this.editorcore;
26384 var editor= this.editor;
26387 var btn = function(id,cmd , toggle, handler, html){
26389 var event = toggle ? 'toggle' : 'click';
26394 xns: Roo.bootstrap,
26398 enableToggle:toggle !== false,
26400 pressed : toggle ? false : null,
26403 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26404 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26410 // var cb_box = function...
26415 xns: Roo.bootstrap,
26420 xns: Roo.bootstrap,
26424 Roo.each(this.formats, function(f) {
26425 style.menu.items.push({
26427 xns: Roo.bootstrap,
26428 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26433 editorcore.insertTag(this.tagname);
26440 children.push(style);
26442 btn('bold',false,true);
26443 btn('italic',false,true);
26444 btn('align-left', 'justifyleft',true);
26445 btn('align-center', 'justifycenter',true);
26446 btn('align-right' , 'justifyright',true);
26447 btn('link', false, false, function(btn) {
26448 //Roo.log("create link?");
26449 var url = prompt(this.createLinkText, this.defaultLinkValue);
26450 if(url && url != 'http:/'+'/'){
26451 this.editorcore.relayCmd('createlink', url);
26454 btn('list','insertunorderedlist',true);
26455 btn('pencil', false,true, function(btn){
26457 this.toggleSourceEdit(btn.pressed);
26460 if (this.editor.btns.length > 0) {
26461 for (var i = 0; i<this.editor.btns.length; i++) {
26462 children.push(this.editor.btns[i]);
26470 xns: Roo.bootstrap,
26475 xns: Roo.bootstrap,
26480 cog.menu.items.push({
26482 xns: Roo.bootstrap,
26483 html : Clean styles,
26488 editorcore.insertTag(this.tagname);
26497 this.xtype = 'NavSimplebar';
26499 for(var i=0;i< children.length;i++) {
26501 this.buttons.add(this.addxtypeChild(children[i]));
26505 editor.on('editorevent', this.updateToolbar, this);
26507 onBtnClick : function(id)
26509 this.editorcore.relayCmd(id);
26510 this.editorcore.focus();
26514 * Protected method that will not generally be called directly. It triggers
26515 * a toolbar update by reading the markup state of the current selection in the editor.
26517 updateToolbar: function(){
26519 if(!this.editorcore.activated){
26520 this.editor.onFirstFocus(); // is this neeed?
26524 var btns = this.buttons;
26525 var doc = this.editorcore.doc;
26526 btns.get('bold').setActive(doc.queryCommandState('bold'));
26527 btns.get('italic').setActive(doc.queryCommandState('italic'));
26528 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26530 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26531 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26532 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26534 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26535 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26538 var ans = this.editorcore.getAllAncestors();
26539 if (this.formatCombo) {
26542 var store = this.formatCombo.store;
26543 this.formatCombo.setValue("");
26544 for (var i =0; i < ans.length;i++) {
26545 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26547 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26555 // hides menus... - so this cant be on a menu...
26556 Roo.bootstrap.MenuMgr.hideAll();
26558 Roo.bootstrap.MenuMgr.hideAll();
26559 //this.editorsyncValue();
26561 onFirstFocus: function() {
26562 this.buttons.each(function(item){
26566 toggleSourceEdit : function(sourceEditMode){
26569 if(sourceEditMode){
26570 Roo.log("disabling buttons");
26571 this.buttons.each( function(item){
26572 if(item.cmd != 'pencil'){
26578 Roo.log("enabling buttons");
26579 if(this.editorcore.initialized){
26580 this.buttons.each( function(item){
26586 Roo.log("calling toggole on editor");
26587 // tell the editor that it's been pressed..
26588 this.editor.toggleSourceEdit(sourceEditMode);
26602 * @class Roo.bootstrap.Markdown
26603 * @extends Roo.bootstrap.TextArea
26604 * Bootstrap Showdown editable area
26605 * @cfg {string} content
26608 * Create a new Showdown
26611 Roo.bootstrap.Markdown = function(config){
26612 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26616 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26620 initEvents : function()
26623 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26624 this.markdownEl = this.el.createChild({
26625 cls : 'roo-markdown-area'
26627 this.inputEl().addClass('d-none');
26628 if (this.getValue() == '') {
26629 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26632 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26634 this.markdownEl.on('click', this.toggleTextEdit, this);
26635 this.on('blur', this.toggleTextEdit, this);
26636 this.on('specialkey', this.resizeTextArea, this);
26639 toggleTextEdit : function()
26641 var sh = this.markdownEl.getHeight();
26642 this.inputEl().addClass('d-none');
26643 this.markdownEl.addClass('d-none');
26644 if (!this.editing) {
26646 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26647 this.inputEl().removeClass('d-none');
26648 this.inputEl().focus();
26649 this.editing = true;
26652 // show showdown...
26653 this.updateMarkdown();
26654 this.markdownEl.removeClass('d-none');
26655 this.editing = false;
26658 updateMarkdown : function()
26660 if (this.getValue() == '') {
26661 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26665 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26668 resizeTextArea: function () {
26671 Roo.log([sh, this.getValue().split("\n").length * 30]);
26672 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26674 setValue : function(val)
26676 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26677 if (!this.editing) {
26678 this.updateMarkdown();
26684 if (!this.editing) {
26685 this.toggleTextEdit();
26693 * @class Roo.bootstrap.Table.AbstractSelectionModel
26694 * @extends Roo.util.Observable
26695 * Abstract base class for grid SelectionModels. It provides the interface that should be
26696 * implemented by descendant classes. This class should not be directly instantiated.
26699 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26700 this.locked = false;
26701 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26705 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26706 /** @ignore Called by the grid automatically. Do not call directly. */
26707 init : function(grid){
26713 * Locks the selections.
26716 this.locked = true;
26720 * Unlocks the selections.
26722 unlock : function(){
26723 this.locked = false;
26727 * Returns true if the selections are locked.
26728 * @return {Boolean}
26730 isLocked : function(){
26731 return this.locked;
26735 initEvents : function ()
26741 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26742 * @class Roo.bootstrap.Table.RowSelectionModel
26743 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26744 * It supports multiple selections and keyboard selection/navigation.
26746 * @param {Object} config
26749 Roo.bootstrap.Table.RowSelectionModel = function(config){
26750 Roo.apply(this, config);
26751 this.selections = new Roo.util.MixedCollection(false, function(o){
26756 this.lastActive = false;
26760 * @event selectionchange
26761 * Fires when the selection changes
26762 * @param {SelectionModel} this
26764 "selectionchange" : true,
26766 * @event afterselectionchange
26767 * Fires after the selection changes (eg. by key press or clicking)
26768 * @param {SelectionModel} this
26770 "afterselectionchange" : true,
26772 * @event beforerowselect
26773 * Fires when a row is selected being selected, return false to cancel.
26774 * @param {SelectionModel} this
26775 * @param {Number} rowIndex The selected index
26776 * @param {Boolean} keepExisting False if other selections will be cleared
26778 "beforerowselect" : true,
26781 * Fires when a row is selected.
26782 * @param {SelectionModel} this
26783 * @param {Number} rowIndex The selected index
26784 * @param {Roo.data.Record} r The record
26786 "rowselect" : true,
26788 * @event rowdeselect
26789 * Fires when a row is deselected.
26790 * @param {SelectionModel} this
26791 * @param {Number} rowIndex The selected index
26793 "rowdeselect" : true
26795 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26796 this.locked = false;
26799 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26801 * @cfg {Boolean} singleSelect
26802 * True to allow selection of only one row at a time (defaults to false)
26804 singleSelect : false,
26807 initEvents : function()
26810 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26811 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26812 //}else{ // allow click to work like normal
26813 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26815 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26816 this.grid.on("rowclick", this.handleMouseDown, this);
26818 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26819 "up" : function(e){
26821 this.selectPrevious(e.shiftKey);
26822 }else if(this.last !== false && this.lastActive !== false){
26823 var last = this.last;
26824 this.selectRange(this.last, this.lastActive-1);
26825 this.grid.getView().focusRow(this.lastActive);
26826 if(last !== false){
26830 this.selectFirstRow();
26832 this.fireEvent("afterselectionchange", this);
26834 "down" : function(e){
26836 this.selectNext(e.shiftKey);
26837 }else if(this.last !== false && this.lastActive !== false){
26838 var last = this.last;
26839 this.selectRange(this.last, this.lastActive+1);
26840 this.grid.getView().focusRow(this.lastActive);
26841 if(last !== false){
26845 this.selectFirstRow();
26847 this.fireEvent("afterselectionchange", this);
26851 this.grid.store.on('load', function(){
26852 this.selections.clear();
26855 var view = this.grid.view;
26856 view.on("refresh", this.onRefresh, this);
26857 view.on("rowupdated", this.onRowUpdated, this);
26858 view.on("rowremoved", this.onRemove, this);
26863 onRefresh : function()
26865 var ds = this.grid.store, i, v = this.grid.view;
26866 var s = this.selections;
26867 s.each(function(r){
26868 if((i = ds.indexOfId(r.id)) != -1){
26877 onRemove : function(v, index, r){
26878 this.selections.remove(r);
26882 onRowUpdated : function(v, index, r){
26883 if(this.isSelected(r)){
26884 v.onRowSelect(index);
26890 * @param {Array} records The records to select
26891 * @param {Boolean} keepExisting (optional) True to keep existing selections
26893 selectRecords : function(records, keepExisting)
26896 this.clearSelections();
26898 var ds = this.grid.store;
26899 for(var i = 0, len = records.length; i < len; i++){
26900 this.selectRow(ds.indexOf(records[i]), true);
26905 * Gets the number of selected rows.
26908 getCount : function(){
26909 return this.selections.length;
26913 * Selects the first row in the grid.
26915 selectFirstRow : function(){
26920 * Select the last row.
26921 * @param {Boolean} keepExisting (optional) True to keep existing selections
26923 selectLastRow : function(keepExisting){
26924 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26925 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26929 * Selects the row immediately following the last selected row.
26930 * @param {Boolean} keepExisting (optional) True to keep existing selections
26932 selectNext : function(keepExisting)
26934 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26935 this.selectRow(this.last+1, keepExisting);
26936 this.grid.getView().focusRow(this.last);
26941 * Selects the row that precedes the last selected row.
26942 * @param {Boolean} keepExisting (optional) True to keep existing selections
26944 selectPrevious : function(keepExisting){
26946 this.selectRow(this.last-1, keepExisting);
26947 this.grid.getView().focusRow(this.last);
26952 * Returns the selected records
26953 * @return {Array} Array of selected records
26955 getSelections : function(){
26956 return [].concat(this.selections.items);
26960 * Returns the first selected record.
26963 getSelected : function(){
26964 return this.selections.itemAt(0);
26969 * Clears all selections.
26971 clearSelections : function(fast)
26977 var ds = this.grid.store;
26978 var s = this.selections;
26979 s.each(function(r){
26980 this.deselectRow(ds.indexOfId(r.id));
26984 this.selections.clear();
26991 * Selects all rows.
26993 selectAll : function(){
26997 this.selections.clear();
26998 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26999 this.selectRow(i, true);
27004 * Returns True if there is a selection.
27005 * @return {Boolean}
27007 hasSelection : function(){
27008 return this.selections.length > 0;
27012 * Returns True if the specified row is selected.
27013 * @param {Number/Record} record The record or index of the record to check
27014 * @return {Boolean}
27016 isSelected : function(index){
27017 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27018 return (r && this.selections.key(r.id) ? true : false);
27022 * Returns True if the specified record id is selected.
27023 * @param {String} id The id of record to check
27024 * @return {Boolean}
27026 isIdSelected : function(id){
27027 return (this.selections.key(id) ? true : false);
27032 handleMouseDBClick : function(e, t){
27036 handleMouseDown : function(e, t)
27038 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27039 if(this.isLocked() || rowIndex < 0 ){
27042 if(e.shiftKey && this.last !== false){
27043 var last = this.last;
27044 this.selectRange(last, rowIndex, e.ctrlKey);
27045 this.last = last; // reset the last
27049 var isSelected = this.isSelected(rowIndex);
27050 //Roo.log("select row:" + rowIndex);
27052 this.deselectRow(rowIndex);
27054 this.selectRow(rowIndex, true);
27058 if(e.button !== 0 && isSelected){
27059 alert('rowIndex 2: ' + rowIndex);
27060 view.focusRow(rowIndex);
27061 }else if(e.ctrlKey && isSelected){
27062 this.deselectRow(rowIndex);
27063 }else if(!isSelected){
27064 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27065 view.focusRow(rowIndex);
27069 this.fireEvent("afterselectionchange", this);
27072 handleDragableRowClick : function(grid, rowIndex, e)
27074 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27075 this.selectRow(rowIndex, false);
27076 grid.view.focusRow(rowIndex);
27077 this.fireEvent("afterselectionchange", this);
27082 * Selects multiple rows.
27083 * @param {Array} rows Array of the indexes of the row to select
27084 * @param {Boolean} keepExisting (optional) True to keep existing selections
27086 selectRows : function(rows, keepExisting){
27088 this.clearSelections();
27090 for(var i = 0, len = rows.length; i < len; i++){
27091 this.selectRow(rows[i], true);
27096 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27097 * @param {Number} startRow The index of the first row in the range
27098 * @param {Number} endRow The index of the last row in the range
27099 * @param {Boolean} keepExisting (optional) True to retain existing selections
27101 selectRange : function(startRow, endRow, keepExisting){
27106 this.clearSelections();
27108 if(startRow <= endRow){
27109 for(var i = startRow; i <= endRow; i++){
27110 this.selectRow(i, true);
27113 for(var i = startRow; i >= endRow; i--){
27114 this.selectRow(i, true);
27120 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27121 * @param {Number} startRow The index of the first row in the range
27122 * @param {Number} endRow The index of the last row in the range
27124 deselectRange : function(startRow, endRow, preventViewNotify){
27128 for(var i = startRow; i <= endRow; i++){
27129 this.deselectRow(i, preventViewNotify);
27135 * @param {Number} row The index of the row to select
27136 * @param {Boolean} keepExisting (optional) True to keep existing selections
27138 selectRow : function(index, keepExisting, preventViewNotify)
27140 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27143 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27144 if(!keepExisting || this.singleSelect){
27145 this.clearSelections();
27148 var r = this.grid.store.getAt(index);
27149 //console.log('selectRow - record id :' + r.id);
27151 this.selections.add(r);
27152 this.last = this.lastActive = index;
27153 if(!preventViewNotify){
27154 var proxy = new Roo.Element(
27155 this.grid.getRowDom(index)
27157 proxy.addClass('bg-info info');
27159 this.fireEvent("rowselect", this, index, r);
27160 this.fireEvent("selectionchange", this);
27166 * @param {Number} row The index of the row to deselect
27168 deselectRow : function(index, preventViewNotify)
27173 if(this.last == index){
27176 if(this.lastActive == index){
27177 this.lastActive = false;
27180 var r = this.grid.store.getAt(index);
27185 this.selections.remove(r);
27186 //.console.log('deselectRow - record id :' + r.id);
27187 if(!preventViewNotify){
27189 var proxy = new Roo.Element(
27190 this.grid.getRowDom(index)
27192 proxy.removeClass('bg-info info');
27194 this.fireEvent("rowdeselect", this, index);
27195 this.fireEvent("selectionchange", this);
27199 restoreLast : function(){
27201 this.last = this._last;
27206 acceptsNav : function(row, col, cm){
27207 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27211 onEditorKey : function(field, e){
27212 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27217 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27219 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27221 }else if(k == e.ENTER && !e.ctrlKey){
27225 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27227 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27229 }else if(k == e.ESC){
27233 g.startEditing(newCell[0], newCell[1]);
27239 * Ext JS Library 1.1.1
27240 * Copyright(c) 2006-2007, Ext JS, LLC.
27242 * Originally Released Under LGPL - original licence link has changed is not relivant.
27245 * <script type="text/javascript">
27249 * @class Roo.bootstrap.PagingToolbar
27250 * @extends Roo.bootstrap.NavSimplebar
27251 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27253 * Create a new PagingToolbar
27254 * @param {Object} config The config object
27255 * @param {Roo.data.Store} store
27257 Roo.bootstrap.PagingToolbar = function(config)
27259 // old args format still supported... - xtype is prefered..
27260 // created from xtype...
27262 this.ds = config.dataSource;
27264 if (config.store && !this.ds) {
27265 this.store= Roo.factory(config.store, Roo.data);
27266 this.ds = this.store;
27267 this.ds.xmodule = this.xmodule || false;
27270 this.toolbarItems = [];
27271 if (config.items) {
27272 this.toolbarItems = config.items;
27275 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27280 this.bind(this.ds);
27283 if (Roo.bootstrap.version == 4) {
27284 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27286 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27291 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27293 * @cfg {Roo.data.Store} dataSource
27294 * The underlying data store providing the paged data
27297 * @cfg {String/HTMLElement/Element} container
27298 * container The id or element that will contain the toolbar
27301 * @cfg {Boolean} displayInfo
27302 * True to display the displayMsg (defaults to false)
27305 * @cfg {Number} pageSize
27306 * The number of records to display per page (defaults to 20)
27310 * @cfg {String} displayMsg
27311 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27313 displayMsg : 'Displaying {0} - {1} of {2}',
27315 * @cfg {String} emptyMsg
27316 * The message to display when no records are found (defaults to "No data to display")
27318 emptyMsg : 'No data to display',
27320 * Customizable piece of the default paging text (defaults to "Page")
27323 beforePageText : "Page",
27325 * Customizable piece of the default paging text (defaults to "of %0")
27328 afterPageText : "of {0}",
27330 * Customizable piece of the default paging text (defaults to "First Page")
27333 firstText : "First Page",
27335 * Customizable piece of the default paging text (defaults to "Previous Page")
27338 prevText : "Previous Page",
27340 * Customizable piece of the default paging text (defaults to "Next Page")
27343 nextText : "Next Page",
27345 * Customizable piece of the default paging text (defaults to "Last Page")
27348 lastText : "Last Page",
27350 * Customizable piece of the default paging text (defaults to "Refresh")
27353 refreshText : "Refresh",
27357 onRender : function(ct, position)
27359 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27360 this.navgroup.parentId = this.id;
27361 this.navgroup.onRender(this.el, null);
27362 // add the buttons to the navgroup
27364 if(this.displayInfo){
27365 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27366 this.displayEl = this.el.select('.x-paging-info', true).first();
27367 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27368 // this.displayEl = navel.el.select('span',true).first();
27374 Roo.each(_this.buttons, function(e){ // this might need to use render????
27375 Roo.factory(e).render(_this.el);
27379 Roo.each(_this.toolbarItems, function(e) {
27380 _this.navgroup.addItem(e);
27384 this.first = this.navgroup.addItem({
27385 tooltip: this.firstText,
27386 cls: "prev btn-outline-secondary",
27387 html : ' <i class="fa fa-step-backward"></i>',
27389 preventDefault: true,
27390 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27393 this.prev = this.navgroup.addItem({
27394 tooltip: this.prevText,
27395 cls: "prev btn-outline-secondary",
27396 html : ' <i class="fa fa-backward"></i>',
27398 preventDefault: true,
27399 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27401 //this.addSeparator();
27404 var field = this.navgroup.addItem( {
27406 cls : 'x-paging-position btn-outline-secondary',
27408 html : this.beforePageText +
27409 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27410 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27413 this.field = field.el.select('input', true).first();
27414 this.field.on("keydown", this.onPagingKeydown, this);
27415 this.field.on("focus", function(){this.dom.select();});
27418 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27419 //this.field.setHeight(18);
27420 //this.addSeparator();
27421 this.next = this.navgroup.addItem({
27422 tooltip: this.nextText,
27423 cls: "next btn-outline-secondary",
27424 html : ' <i class="fa fa-forward"></i>',
27426 preventDefault: true,
27427 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27429 this.last = this.navgroup.addItem({
27430 tooltip: this.lastText,
27431 html : ' <i class="fa fa-step-forward"></i>',
27432 cls: "next btn-outline-secondary",
27434 preventDefault: true,
27435 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27437 //this.addSeparator();
27438 this.loading = this.navgroup.addItem({
27439 tooltip: this.refreshText,
27440 cls: "btn-outline-secondary",
27441 html : ' <i class="fa fa-refresh"></i>',
27442 preventDefault: true,
27443 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27449 updateInfo : function(){
27450 if(this.displayEl){
27451 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27452 var msg = count == 0 ?
27456 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27458 this.displayEl.update(msg);
27463 onLoad : function(ds, r, o)
27465 this.cursor = o.params && o.params.start ? o.params.start : 0;
27467 var d = this.getPageData(),
27472 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27473 this.field.dom.value = ap;
27474 this.first.setDisabled(ap == 1);
27475 this.prev.setDisabled(ap == 1);
27476 this.next.setDisabled(ap == ps);
27477 this.last.setDisabled(ap == ps);
27478 this.loading.enable();
27483 getPageData : function(){
27484 var total = this.ds.getTotalCount();
27487 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27488 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27493 onLoadError : function(){
27494 this.loading.enable();
27498 onPagingKeydown : function(e){
27499 var k = e.getKey();
27500 var d = this.getPageData();
27502 var v = this.field.dom.value, pageNum;
27503 if(!v || isNaN(pageNum = parseInt(v, 10))){
27504 this.field.dom.value = d.activePage;
27507 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27508 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27511 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))
27513 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27514 this.field.dom.value = pageNum;
27515 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27518 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27520 var v = this.field.dom.value, pageNum;
27521 var increment = (e.shiftKey) ? 10 : 1;
27522 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27525 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27526 this.field.dom.value = d.activePage;
27529 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27531 this.field.dom.value = parseInt(v, 10) + increment;
27532 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27533 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27540 beforeLoad : function(){
27542 this.loading.disable();
27547 onClick : function(which){
27556 ds.load({params:{start: 0, limit: this.pageSize}});
27559 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27562 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27565 var total = ds.getTotalCount();
27566 var extra = total % this.pageSize;
27567 var lastStart = extra ? (total - extra) : total-this.pageSize;
27568 ds.load({params:{start: lastStart, limit: this.pageSize}});
27571 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27577 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27578 * @param {Roo.data.Store} store The data store to unbind
27580 unbind : function(ds){
27581 ds.un("beforeload", this.beforeLoad, this);
27582 ds.un("load", this.onLoad, this);
27583 ds.un("loadexception", this.onLoadError, this);
27584 ds.un("remove", this.updateInfo, this);
27585 ds.un("add", this.updateInfo, this);
27586 this.ds = undefined;
27590 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27591 * @param {Roo.data.Store} store The data store to bind
27593 bind : function(ds){
27594 ds.on("beforeload", this.beforeLoad, this);
27595 ds.on("load", this.onLoad, this);
27596 ds.on("loadexception", this.onLoadError, this);
27597 ds.on("remove", this.updateInfo, this);
27598 ds.on("add", this.updateInfo, this);
27609 * @class Roo.bootstrap.MessageBar
27610 * @extends Roo.bootstrap.Component
27611 * Bootstrap MessageBar class
27612 * @cfg {String} html contents of the MessageBar
27613 * @cfg {String} weight (info | success | warning | danger) default info
27614 * @cfg {String} beforeClass insert the bar before the given class
27615 * @cfg {Boolean} closable (true | false) default false
27616 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27619 * Create a new Element
27620 * @param {Object} config The config object
27623 Roo.bootstrap.MessageBar = function(config){
27624 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27627 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27633 beforeClass: 'bootstrap-sticky-wrap',
27635 getAutoCreate : function(){
27639 cls: 'alert alert-dismissable alert-' + this.weight,
27644 html: this.html || ''
27650 cfg.cls += ' alert-messages-fixed';
27664 onRender : function(ct, position)
27666 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27669 var cfg = Roo.apply({}, this.getAutoCreate());
27673 cfg.cls += ' ' + this.cls;
27676 cfg.style = this.style;
27678 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27680 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27683 this.el.select('>button.close').on('click', this.hide, this);
27689 if (!this.rendered) {
27695 this.fireEvent('show', this);
27701 if (!this.rendered) {
27707 this.fireEvent('hide', this);
27710 update : function()
27712 // var e = this.el.dom.firstChild;
27714 // if(this.closable){
27715 // e = e.nextSibling;
27718 // e.data = this.html || '';
27720 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27736 * @class Roo.bootstrap.Graph
27737 * @extends Roo.bootstrap.Component
27738 * Bootstrap Graph class
27742 @cfg {String} graphtype bar | vbar | pie
27743 @cfg {number} g_x coodinator | centre x (pie)
27744 @cfg {number} g_y coodinator | centre y (pie)
27745 @cfg {number} g_r radius (pie)
27746 @cfg {number} g_height height of the chart (respected by all elements in the set)
27747 @cfg {number} g_width width of the chart (respected by all elements in the set)
27748 @cfg {Object} title The title of the chart
27751 -opts (object) options for the chart
27753 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27754 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27756 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.
27757 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27759 o stretch (boolean)
27761 -opts (object) options for the pie
27764 o startAngle (number)
27765 o endAngle (number)
27769 * Create a new Input
27770 * @param {Object} config The config object
27773 Roo.bootstrap.Graph = function(config){
27774 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27780 * The img click event for the img.
27781 * @param {Roo.EventObject} e
27787 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27798 //g_colors: this.colors,
27805 getAutoCreate : function(){
27816 onRender : function(ct,position){
27819 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27821 if (typeof(Raphael) == 'undefined') {
27822 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27826 this.raphael = Raphael(this.el.dom);
27828 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27829 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27830 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27831 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27833 r.text(160, 10, "Single Series Chart").attr(txtattr);
27834 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27835 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27836 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27838 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27839 r.barchart(330, 10, 300, 220, data1);
27840 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27841 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27844 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27845 // r.barchart(30, 30, 560, 250, xdata, {
27846 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27847 // axis : "0 0 1 1",
27848 // axisxlabels : xdata
27849 // //yvalues : cols,
27852 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27854 // this.load(null,xdata,{
27855 // axis : "0 0 1 1",
27856 // axisxlabels : xdata
27861 load : function(graphtype,xdata,opts)
27863 this.raphael.clear();
27865 graphtype = this.graphtype;
27870 var r = this.raphael,
27871 fin = function () {
27872 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27874 fout = function () {
27875 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27877 pfin = function() {
27878 this.sector.stop();
27879 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27882 this.label[0].stop();
27883 this.label[0].attr({ r: 7.5 });
27884 this.label[1].attr({ "font-weight": 800 });
27887 pfout = function() {
27888 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27891 this.label[0].animate({ r: 5 }, 500, "bounce");
27892 this.label[1].attr({ "font-weight": 400 });
27898 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27901 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27904 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27905 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27907 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27914 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27919 setTitle: function(o)
27924 initEvents: function() {
27927 this.el.on('click', this.onClick, this);
27931 onClick : function(e)
27933 Roo.log('img onclick');
27934 this.fireEvent('click', this, e);
27946 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27949 * @class Roo.bootstrap.dash.NumberBox
27950 * @extends Roo.bootstrap.Component
27951 * Bootstrap NumberBox class
27952 * @cfg {String} headline Box headline
27953 * @cfg {String} content Box content
27954 * @cfg {String} icon Box icon
27955 * @cfg {String} footer Footer text
27956 * @cfg {String} fhref Footer href
27959 * Create a new NumberBox
27960 * @param {Object} config The config object
27964 Roo.bootstrap.dash.NumberBox = function(config){
27965 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27969 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27978 getAutoCreate : function(){
27982 cls : 'small-box ',
27990 cls : 'roo-headline',
27991 html : this.headline
27995 cls : 'roo-content',
27996 html : this.content
28010 cls : 'ion ' + this.icon
28019 cls : 'small-box-footer',
28020 href : this.fhref || '#',
28024 cfg.cn.push(footer);
28031 onRender : function(ct,position){
28032 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28039 setHeadline: function (value)
28041 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28044 setFooter: function (value, href)
28046 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28049 this.el.select('a.small-box-footer',true).first().attr('href', href);
28054 setContent: function (value)
28056 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28059 initEvents: function()
28073 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28076 * @class Roo.bootstrap.dash.TabBox
28077 * @extends Roo.bootstrap.Component
28078 * Bootstrap TabBox class
28079 * @cfg {String} title Title of the TabBox
28080 * @cfg {String} icon Icon of the TabBox
28081 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28082 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28085 * Create a new TabBox
28086 * @param {Object} config The config object
28090 Roo.bootstrap.dash.TabBox = function(config){
28091 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28096 * When a pane is added
28097 * @param {Roo.bootstrap.dash.TabPane} pane
28101 * @event activatepane
28102 * When a pane is activated
28103 * @param {Roo.bootstrap.dash.TabPane} pane
28105 "activatepane" : true
28113 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28118 tabScrollable : false,
28120 getChildContainer : function()
28122 return this.el.select('.tab-content', true).first();
28125 getAutoCreate : function(){
28129 cls: 'pull-left header',
28137 cls: 'fa ' + this.icon
28143 cls: 'nav nav-tabs pull-right',
28149 if(this.tabScrollable){
28156 cls: 'nav nav-tabs pull-right',
28167 cls: 'nav-tabs-custom',
28172 cls: 'tab-content no-padding',
28180 initEvents : function()
28182 //Roo.log('add add pane handler');
28183 this.on('addpane', this.onAddPane, this);
28186 * Updates the box title
28187 * @param {String} html to set the title to.
28189 setTitle : function(value)
28191 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28193 onAddPane : function(pane)
28195 this.panes.push(pane);
28196 //Roo.log('addpane');
28198 // tabs are rendere left to right..
28199 if(!this.showtabs){
28203 var ctr = this.el.select('.nav-tabs', true).first();
28206 var existing = ctr.select('.nav-tab',true);
28207 var qty = existing.getCount();;
28210 var tab = ctr.createChild({
28212 cls : 'nav-tab' + (qty ? '' : ' active'),
28220 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28223 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28225 pane.el.addClass('active');
28230 onTabClick : function(ev,un,ob,pane)
28232 //Roo.log('tab - prev default');
28233 ev.preventDefault();
28236 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28237 pane.tab.addClass('active');
28238 //Roo.log(pane.title);
28239 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28240 // technically we should have a deactivate event.. but maybe add later.
28241 // and it should not de-activate the selected tab...
28242 this.fireEvent('activatepane', pane);
28243 pane.el.addClass('active');
28244 pane.fireEvent('activate');
28249 getActivePane : function()
28252 Roo.each(this.panes, function(p) {
28253 if(p.el.hasClass('active')){
28274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28276 * @class Roo.bootstrap.TabPane
28277 * @extends Roo.bootstrap.Component
28278 * Bootstrap TabPane class
28279 * @cfg {Boolean} active (false | true) Default false
28280 * @cfg {String} title title of panel
28284 * Create a new TabPane
28285 * @param {Object} config The config object
28288 Roo.bootstrap.dash.TabPane = function(config){
28289 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28295 * When a pane is activated
28296 * @param {Roo.bootstrap.dash.TabPane} pane
28303 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28308 // the tabBox that this is attached to.
28311 getAutoCreate : function()
28319 cfg.cls += ' active';
28324 initEvents : function()
28326 //Roo.log('trigger add pane handler');
28327 this.parent().fireEvent('addpane', this)
28331 * Updates the tab title
28332 * @param {String} html to set the title to.
28334 setTitle: function(str)
28340 this.tab.select('a', true).first().dom.innerHTML = str;
28357 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28360 * @class Roo.bootstrap.menu.Menu
28361 * @extends Roo.bootstrap.Component
28362 * Bootstrap Menu class - container for Menu
28363 * @cfg {String} html Text of the menu
28364 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28365 * @cfg {String} icon Font awesome icon
28366 * @cfg {String} pos Menu align to (top | bottom) default bottom
28370 * Create a new Menu
28371 * @param {Object} config The config object
28375 Roo.bootstrap.menu.Menu = function(config){
28376 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28380 * @event beforeshow
28381 * Fires before this menu is displayed
28382 * @param {Roo.bootstrap.menu.Menu} this
28386 * @event beforehide
28387 * Fires before this menu is hidden
28388 * @param {Roo.bootstrap.menu.Menu} this
28393 * Fires after this menu is displayed
28394 * @param {Roo.bootstrap.menu.Menu} this
28399 * Fires after this menu is hidden
28400 * @param {Roo.bootstrap.menu.Menu} this
28405 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28406 * @param {Roo.bootstrap.menu.Menu} this
28407 * @param {Roo.EventObject} e
28414 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28418 weight : 'default',
28423 getChildContainer : function() {
28424 if(this.isSubMenu){
28428 return this.el.select('ul.dropdown-menu', true).first();
28431 getAutoCreate : function()
28436 cls : 'roo-menu-text',
28444 cls : 'fa ' + this.icon
28455 cls : 'dropdown-button btn btn-' + this.weight,
28460 cls : 'dropdown-toggle btn btn-' + this.weight,
28470 cls : 'dropdown-menu'
28476 if(this.pos == 'top'){
28477 cfg.cls += ' dropup';
28480 if(this.isSubMenu){
28483 cls : 'dropdown-menu'
28490 onRender : function(ct, position)
28492 this.isSubMenu = ct.hasClass('dropdown-submenu');
28494 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28497 initEvents : function()
28499 if(this.isSubMenu){
28503 this.hidden = true;
28505 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28506 this.triggerEl.on('click', this.onTriggerPress, this);
28508 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28509 this.buttonEl.on('click', this.onClick, this);
28515 if(this.isSubMenu){
28519 return this.el.select('ul.dropdown-menu', true).first();
28522 onClick : function(e)
28524 this.fireEvent("click", this, e);
28527 onTriggerPress : function(e)
28529 if (this.isVisible()) {
28536 isVisible : function(){
28537 return !this.hidden;
28542 this.fireEvent("beforeshow", this);
28544 this.hidden = false;
28545 this.el.addClass('open');
28547 Roo.get(document).on("mouseup", this.onMouseUp, this);
28549 this.fireEvent("show", this);
28556 this.fireEvent("beforehide", this);
28558 this.hidden = true;
28559 this.el.removeClass('open');
28561 Roo.get(document).un("mouseup", this.onMouseUp);
28563 this.fireEvent("hide", this);
28566 onMouseUp : function()
28580 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28583 * @class Roo.bootstrap.menu.Item
28584 * @extends Roo.bootstrap.Component
28585 * Bootstrap MenuItem class
28586 * @cfg {Boolean} submenu (true | false) default false
28587 * @cfg {String} html text of the item
28588 * @cfg {String} href the link
28589 * @cfg {Boolean} disable (true | false) default false
28590 * @cfg {Boolean} preventDefault (true | false) default true
28591 * @cfg {String} icon Font awesome icon
28592 * @cfg {String} pos Submenu align to (left | right) default right
28596 * Create a new Item
28597 * @param {Object} config The config object
28601 Roo.bootstrap.menu.Item = function(config){
28602 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28606 * Fires when the mouse is hovering over this menu
28607 * @param {Roo.bootstrap.menu.Item} this
28608 * @param {Roo.EventObject} e
28613 * Fires when the mouse exits this menu
28614 * @param {Roo.bootstrap.menu.Item} this
28615 * @param {Roo.EventObject} e
28621 * The raw click event for the entire grid.
28622 * @param {Roo.EventObject} e
28628 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28633 preventDefault: true,
28638 getAutoCreate : function()
28643 cls : 'roo-menu-item-text',
28651 cls : 'fa ' + this.icon
28660 href : this.href || '#',
28667 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28671 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28673 if(this.pos == 'left'){
28674 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28681 initEvents : function()
28683 this.el.on('mouseover', this.onMouseOver, this);
28684 this.el.on('mouseout', this.onMouseOut, this);
28686 this.el.select('a', true).first().on('click', this.onClick, this);
28690 onClick : function(e)
28692 if(this.preventDefault){
28693 e.preventDefault();
28696 this.fireEvent("click", this, e);
28699 onMouseOver : function(e)
28701 if(this.submenu && this.pos == 'left'){
28702 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28705 this.fireEvent("mouseover", this, e);
28708 onMouseOut : function(e)
28710 this.fireEvent("mouseout", this, e);
28722 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28725 * @class Roo.bootstrap.menu.Separator
28726 * @extends Roo.bootstrap.Component
28727 * Bootstrap Separator class
28730 * Create a new Separator
28731 * @param {Object} config The config object
28735 Roo.bootstrap.menu.Separator = function(config){
28736 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28739 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28741 getAutoCreate : function(){
28762 * @class Roo.bootstrap.Tooltip
28763 * Bootstrap Tooltip class
28764 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28765 * to determine which dom element triggers the tooltip.
28767 * It needs to add support for additional attributes like tooltip-position
28770 * Create a new Toolti
28771 * @param {Object} config The config object
28774 Roo.bootstrap.Tooltip = function(config){
28775 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28777 this.alignment = Roo.bootstrap.Tooltip.alignment;
28779 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28780 this.alignment = config.alignment;
28785 Roo.apply(Roo.bootstrap.Tooltip, {
28787 * @function init initialize tooltip monitoring.
28791 currentTip : false,
28792 currentRegion : false,
28798 Roo.get(document).on('mouseover', this.enter ,this);
28799 Roo.get(document).on('mouseout', this.leave, this);
28802 this.currentTip = new Roo.bootstrap.Tooltip();
28805 enter : function(ev)
28807 var dom = ev.getTarget();
28809 //Roo.log(['enter',dom]);
28810 var el = Roo.fly(dom);
28811 if (this.currentEl) {
28813 //Roo.log(this.currentEl);
28814 //Roo.log(this.currentEl.contains(dom));
28815 if (this.currentEl == el) {
28818 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28824 if (this.currentTip.el) {
28825 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28829 if(!el || el.dom == document){
28835 // you can not look for children, as if el is the body.. then everythign is the child..
28836 if (!el.attr('tooltip')) { //
28837 if (!el.select("[tooltip]").elements.length) {
28840 // is the mouse over this child...?
28841 bindEl = el.select("[tooltip]").first();
28842 var xy = ev.getXY();
28843 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28844 //Roo.log("not in region.");
28847 //Roo.log("child element over..");
28850 this.currentEl = bindEl;
28851 this.currentTip.bind(bindEl);
28852 this.currentRegion = Roo.lib.Region.getRegion(dom);
28853 this.currentTip.enter();
28856 leave : function(ev)
28858 var dom = ev.getTarget();
28859 //Roo.log(['leave',dom]);
28860 if (!this.currentEl) {
28865 if (dom != this.currentEl.dom) {
28868 var xy = ev.getXY();
28869 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28872 // only activate leave if mouse cursor is outside... bounding box..
28877 if (this.currentTip) {
28878 this.currentTip.leave();
28880 //Roo.log('clear currentEl');
28881 this.currentEl = false;
28886 'left' : ['r-l', [-2,0], 'right'],
28887 'right' : ['l-r', [2,0], 'left'],
28888 'bottom' : ['t-b', [0,2], 'top'],
28889 'top' : [ 'b-t', [0,-2], 'bottom']
28895 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28900 delay : null, // can be { show : 300 , hide: 500}
28904 hoverState : null, //???
28906 placement : 'bottom',
28910 getAutoCreate : function(){
28917 cls : 'tooltip-arrow arrow'
28920 cls : 'tooltip-inner'
28927 bind : function(el)
28932 initEvents : function()
28934 this.arrowEl = this.el.select('.arrow', true).first();
28935 this.innerEl = this.el.select('.tooltip-inner', true).first();
28938 enter : function () {
28940 if (this.timeout != null) {
28941 clearTimeout(this.timeout);
28944 this.hoverState = 'in';
28945 //Roo.log("enter - show");
28946 if (!this.delay || !this.delay.show) {
28951 this.timeout = setTimeout(function () {
28952 if (_t.hoverState == 'in') {
28955 }, this.delay.show);
28959 clearTimeout(this.timeout);
28961 this.hoverState = 'out';
28962 if (!this.delay || !this.delay.hide) {
28968 this.timeout = setTimeout(function () {
28969 //Roo.log("leave - timeout");
28971 if (_t.hoverState == 'out') {
28973 Roo.bootstrap.Tooltip.currentEl = false;
28978 show : function (msg)
28981 this.render(document.body);
28984 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28986 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28988 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28990 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28991 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28993 var placement = typeof this.placement == 'function' ?
28994 this.placement.call(this, this.el, on_el) :
28997 var autoToken = /\s?auto?\s?/i;
28998 var autoPlace = autoToken.test(placement);
29000 placement = placement.replace(autoToken, '') || 'top';
29004 //this.el.setXY([0,0]);
29006 //this.el.dom.style.display='block';
29008 //this.el.appendTo(on_el);
29010 var p = this.getPosition();
29011 var box = this.el.getBox();
29017 var align = this.alignment[placement];
29019 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29021 if(placement == 'top' || placement == 'bottom'){
29023 placement = 'right';
29026 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29027 placement = 'left';
29030 var scroll = Roo.select('body', true).first().getScroll();
29032 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29036 align = this.alignment[placement];
29038 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29042 this.el.alignTo(this.bindEl, align[0],align[1]);
29043 //var arrow = this.el.select('.arrow',true).first();
29044 //arrow.set(align[2],
29046 this.el.addClass(placement);
29047 this.el.addClass("bs-tooltip-"+ placement);
29049 this.el.addClass('in fade show');
29051 this.hoverState = null;
29053 if (this.el.hasClass('fade')) {
29068 //this.el.setXY([0,0]);
29069 this.el.removeClass(['show', 'in']);
29085 * @class Roo.bootstrap.LocationPicker
29086 * @extends Roo.bootstrap.Component
29087 * Bootstrap LocationPicker class
29088 * @cfg {Number} latitude Position when init default 0
29089 * @cfg {Number} longitude Position when init default 0
29090 * @cfg {Number} zoom default 15
29091 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29092 * @cfg {Boolean} mapTypeControl default false
29093 * @cfg {Boolean} disableDoubleClickZoom default false
29094 * @cfg {Boolean} scrollwheel default true
29095 * @cfg {Boolean} streetViewControl default false
29096 * @cfg {Number} radius default 0
29097 * @cfg {String} locationName
29098 * @cfg {Boolean} draggable default true
29099 * @cfg {Boolean} enableAutocomplete default false
29100 * @cfg {Boolean} enableReverseGeocode default true
29101 * @cfg {String} markerTitle
29104 * Create a new LocationPicker
29105 * @param {Object} config The config object
29109 Roo.bootstrap.LocationPicker = function(config){
29111 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29116 * Fires when the picker initialized.
29117 * @param {Roo.bootstrap.LocationPicker} this
29118 * @param {Google Location} location
29122 * @event positionchanged
29123 * Fires when the picker position changed.
29124 * @param {Roo.bootstrap.LocationPicker} this
29125 * @param {Google Location} location
29127 positionchanged : true,
29130 * Fires when the map resize.
29131 * @param {Roo.bootstrap.LocationPicker} this
29136 * Fires when the map show.
29137 * @param {Roo.bootstrap.LocationPicker} this
29142 * Fires when the map hide.
29143 * @param {Roo.bootstrap.LocationPicker} this
29148 * Fires when click the map.
29149 * @param {Roo.bootstrap.LocationPicker} this
29150 * @param {Map event} e
29154 * @event mapRightClick
29155 * Fires when right click the map.
29156 * @param {Roo.bootstrap.LocationPicker} this
29157 * @param {Map event} e
29159 mapRightClick : true,
29161 * @event markerClick
29162 * Fires when click the marker.
29163 * @param {Roo.bootstrap.LocationPicker} this
29164 * @param {Map event} e
29166 markerClick : true,
29168 * @event markerRightClick
29169 * Fires when right click the marker.
29170 * @param {Roo.bootstrap.LocationPicker} this
29171 * @param {Map event} e
29173 markerRightClick : true,
29175 * @event OverlayViewDraw
29176 * Fires when OverlayView Draw
29177 * @param {Roo.bootstrap.LocationPicker} this
29179 OverlayViewDraw : true,
29181 * @event OverlayViewOnAdd
29182 * Fires when OverlayView Draw
29183 * @param {Roo.bootstrap.LocationPicker} this
29185 OverlayViewOnAdd : true,
29187 * @event OverlayViewOnRemove
29188 * Fires when OverlayView Draw
29189 * @param {Roo.bootstrap.LocationPicker} this
29191 OverlayViewOnRemove : true,
29193 * @event OverlayViewShow
29194 * Fires when OverlayView Draw
29195 * @param {Roo.bootstrap.LocationPicker} this
29196 * @param {Pixel} cpx
29198 OverlayViewShow : true,
29200 * @event OverlayViewHide
29201 * Fires when OverlayView Draw
29202 * @param {Roo.bootstrap.LocationPicker} this
29204 OverlayViewHide : true,
29206 * @event loadexception
29207 * Fires when load google lib failed.
29208 * @param {Roo.bootstrap.LocationPicker} this
29210 loadexception : true
29215 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29217 gMapContext: false,
29223 mapTypeControl: false,
29224 disableDoubleClickZoom: false,
29226 streetViewControl: false,
29230 enableAutocomplete: false,
29231 enableReverseGeocode: true,
29234 getAutoCreate: function()
29239 cls: 'roo-location-picker'
29245 initEvents: function(ct, position)
29247 if(!this.el.getWidth() || this.isApplied()){
29251 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29256 initial: function()
29258 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29259 this.fireEvent('loadexception', this);
29263 if(!this.mapTypeId){
29264 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29267 this.gMapContext = this.GMapContext();
29269 this.initOverlayView();
29271 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29275 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29276 _this.setPosition(_this.gMapContext.marker.position);
29279 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29280 _this.fireEvent('mapClick', this, event);
29284 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29285 _this.fireEvent('mapRightClick', this, event);
29289 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29290 _this.fireEvent('markerClick', this, event);
29294 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29295 _this.fireEvent('markerRightClick', this, event);
29299 this.setPosition(this.gMapContext.location);
29301 this.fireEvent('initial', this, this.gMapContext.location);
29304 initOverlayView: function()
29308 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29312 _this.fireEvent('OverlayViewDraw', _this);
29317 _this.fireEvent('OverlayViewOnAdd', _this);
29320 onRemove: function()
29322 _this.fireEvent('OverlayViewOnRemove', _this);
29325 show: function(cpx)
29327 _this.fireEvent('OverlayViewShow', _this, cpx);
29332 _this.fireEvent('OverlayViewHide', _this);
29338 fromLatLngToContainerPixel: function(event)
29340 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29343 isApplied: function()
29345 return this.getGmapContext() == false ? false : true;
29348 getGmapContext: function()
29350 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29353 GMapContext: function()
29355 var position = new google.maps.LatLng(this.latitude, this.longitude);
29357 var _map = new google.maps.Map(this.el.dom, {
29360 mapTypeId: this.mapTypeId,
29361 mapTypeControl: this.mapTypeControl,
29362 disableDoubleClickZoom: this.disableDoubleClickZoom,
29363 scrollwheel: this.scrollwheel,
29364 streetViewControl: this.streetViewControl,
29365 locationName: this.locationName,
29366 draggable: this.draggable,
29367 enableAutocomplete: this.enableAutocomplete,
29368 enableReverseGeocode: this.enableReverseGeocode
29371 var _marker = new google.maps.Marker({
29372 position: position,
29374 title: this.markerTitle,
29375 draggable: this.draggable
29382 location: position,
29383 radius: this.radius,
29384 locationName: this.locationName,
29385 addressComponents: {
29386 formatted_address: null,
29387 addressLine1: null,
29388 addressLine2: null,
29390 streetNumber: null,
29394 stateOrProvince: null
29397 domContainer: this.el.dom,
29398 geodecoder: new google.maps.Geocoder()
29402 drawCircle: function(center, radius, options)
29404 if (this.gMapContext.circle != null) {
29405 this.gMapContext.circle.setMap(null);
29409 options = Roo.apply({}, options, {
29410 strokeColor: "#0000FF",
29411 strokeOpacity: .35,
29413 fillColor: "#0000FF",
29417 options.map = this.gMapContext.map;
29418 options.radius = radius;
29419 options.center = center;
29420 this.gMapContext.circle = new google.maps.Circle(options);
29421 return this.gMapContext.circle;
29427 setPosition: function(location)
29429 this.gMapContext.location = location;
29430 this.gMapContext.marker.setPosition(location);
29431 this.gMapContext.map.panTo(location);
29432 this.drawCircle(location, this.gMapContext.radius, {});
29436 if (this.gMapContext.settings.enableReverseGeocode) {
29437 this.gMapContext.geodecoder.geocode({
29438 latLng: this.gMapContext.location
29439 }, function(results, status) {
29441 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29442 _this.gMapContext.locationName = results[0].formatted_address;
29443 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29445 _this.fireEvent('positionchanged', this, location);
29452 this.fireEvent('positionchanged', this, location);
29457 google.maps.event.trigger(this.gMapContext.map, "resize");
29459 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29461 this.fireEvent('resize', this);
29464 setPositionByLatLng: function(latitude, longitude)
29466 this.setPosition(new google.maps.LatLng(latitude, longitude));
29469 getCurrentPosition: function()
29472 latitude: this.gMapContext.location.lat(),
29473 longitude: this.gMapContext.location.lng()
29477 getAddressName: function()
29479 return this.gMapContext.locationName;
29482 getAddressComponents: function()
29484 return this.gMapContext.addressComponents;
29487 address_component_from_google_geocode: function(address_components)
29491 for (var i = 0; i < address_components.length; i++) {
29492 var component = address_components[i];
29493 if (component.types.indexOf("postal_code") >= 0) {
29494 result.postalCode = component.short_name;
29495 } else if (component.types.indexOf("street_number") >= 0) {
29496 result.streetNumber = component.short_name;
29497 } else if (component.types.indexOf("route") >= 0) {
29498 result.streetName = component.short_name;
29499 } else if (component.types.indexOf("neighborhood") >= 0) {
29500 result.city = component.short_name;
29501 } else if (component.types.indexOf("locality") >= 0) {
29502 result.city = component.short_name;
29503 } else if (component.types.indexOf("sublocality") >= 0) {
29504 result.district = component.short_name;
29505 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29506 result.stateOrProvince = component.short_name;
29507 } else if (component.types.indexOf("country") >= 0) {
29508 result.country = component.short_name;
29512 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29513 result.addressLine2 = "";
29517 setZoomLevel: function(zoom)
29519 this.gMapContext.map.setZoom(zoom);
29532 this.fireEvent('show', this);
29543 this.fireEvent('hide', this);
29548 Roo.apply(Roo.bootstrap.LocationPicker, {
29550 OverlayView : function(map, options)
29552 options = options || {};
29559 * @class Roo.bootstrap.Alert
29560 * @extends Roo.bootstrap.Component
29561 * Bootstrap Alert class - shows an alert area box
29563 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29564 Enter a valid email address
29567 * @cfg {String} title The title of alert
29568 * @cfg {String} html The content of alert
29569 * @cfg {String} weight ( success | info | warning | danger )
29570 * @cfg {String} faicon font-awesomeicon
29573 * Create a new alert
29574 * @param {Object} config The config object
29578 Roo.bootstrap.Alert = function(config){
29579 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29583 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29590 getAutoCreate : function()
29599 cls : 'roo-alert-icon'
29604 cls : 'roo-alert-title',
29609 cls : 'roo-alert-text',
29616 cfg.cn[0].cls += ' fa ' + this.faicon;
29620 cfg.cls += ' alert-' + this.weight;
29626 initEvents: function()
29628 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29631 setTitle : function(str)
29633 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29636 setText : function(str)
29638 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29641 setWeight : function(weight)
29644 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29647 this.weight = weight;
29649 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29652 setIcon : function(icon)
29655 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29658 this.faicon = icon;
29660 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29681 * @class Roo.bootstrap.UploadCropbox
29682 * @extends Roo.bootstrap.Component
29683 * Bootstrap UploadCropbox class
29684 * @cfg {String} emptyText show when image has been loaded
29685 * @cfg {String} rotateNotify show when image too small to rotate
29686 * @cfg {Number} errorTimeout default 3000
29687 * @cfg {Number} minWidth default 300
29688 * @cfg {Number} minHeight default 300
29689 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29690 * @cfg {Boolean} isDocument (true|false) default false
29691 * @cfg {String} url action url
29692 * @cfg {String} paramName default 'imageUpload'
29693 * @cfg {String} method default POST
29694 * @cfg {Boolean} loadMask (true|false) default true
29695 * @cfg {Boolean} loadingText default 'Loading...'
29698 * Create a new UploadCropbox
29699 * @param {Object} config The config object
29702 Roo.bootstrap.UploadCropbox = function(config){
29703 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29707 * @event beforeselectfile
29708 * Fire before select file
29709 * @param {Roo.bootstrap.UploadCropbox} this
29711 "beforeselectfile" : true,
29714 * Fire after initEvent
29715 * @param {Roo.bootstrap.UploadCropbox} this
29720 * Fire after initEvent
29721 * @param {Roo.bootstrap.UploadCropbox} this
29722 * @param {String} data
29727 * Fire when preparing the file data
29728 * @param {Roo.bootstrap.UploadCropbox} this
29729 * @param {Object} file
29734 * Fire when get exception
29735 * @param {Roo.bootstrap.UploadCropbox} this
29736 * @param {XMLHttpRequest} xhr
29738 "exception" : true,
29740 * @event beforeloadcanvas
29741 * Fire before load the canvas
29742 * @param {Roo.bootstrap.UploadCropbox} this
29743 * @param {String} src
29745 "beforeloadcanvas" : true,
29748 * Fire when trash image
29749 * @param {Roo.bootstrap.UploadCropbox} this
29754 * Fire when download the image
29755 * @param {Roo.bootstrap.UploadCropbox} this
29759 * @event footerbuttonclick
29760 * Fire when footerbuttonclick
29761 * @param {Roo.bootstrap.UploadCropbox} this
29762 * @param {String} type
29764 "footerbuttonclick" : true,
29768 * @param {Roo.bootstrap.UploadCropbox} this
29773 * Fire when rotate the image
29774 * @param {Roo.bootstrap.UploadCropbox} this
29775 * @param {String} pos
29780 * Fire when inspect the file
29781 * @param {Roo.bootstrap.UploadCropbox} this
29782 * @param {Object} file
29787 * Fire when xhr upload the file
29788 * @param {Roo.bootstrap.UploadCropbox} this
29789 * @param {Object} data
29794 * Fire when arrange the file data
29795 * @param {Roo.bootstrap.UploadCropbox} this
29796 * @param {Object} formData
29801 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29804 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29806 emptyText : 'Click to upload image',
29807 rotateNotify : 'Image is too small to rotate',
29808 errorTimeout : 3000,
29822 cropType : 'image/jpeg',
29824 canvasLoaded : false,
29825 isDocument : false,
29827 paramName : 'imageUpload',
29829 loadingText : 'Loading...',
29832 getAutoCreate : function()
29836 cls : 'roo-upload-cropbox',
29840 cls : 'roo-upload-cropbox-selector',
29845 cls : 'roo-upload-cropbox-body',
29846 style : 'cursor:pointer',
29850 cls : 'roo-upload-cropbox-preview'
29854 cls : 'roo-upload-cropbox-thumb'
29858 cls : 'roo-upload-cropbox-empty-notify',
29859 html : this.emptyText
29863 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29864 html : this.rotateNotify
29870 cls : 'roo-upload-cropbox-footer',
29873 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29883 onRender : function(ct, position)
29885 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29887 if (this.buttons.length) {
29889 Roo.each(this.buttons, function(bb) {
29891 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29893 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29899 this.maskEl = this.el;
29903 initEvents : function()
29905 this.urlAPI = (window.createObjectURL && window) ||
29906 (window.URL && URL.revokeObjectURL && URL) ||
29907 (window.webkitURL && webkitURL);
29909 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29910 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29912 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29913 this.selectorEl.hide();
29915 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29916 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29918 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29919 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29920 this.thumbEl.hide();
29922 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29923 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29925 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29926 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29927 this.errorEl.hide();
29929 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29930 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29931 this.footerEl.hide();
29933 this.setThumbBoxSize();
29939 this.fireEvent('initial', this);
29946 window.addEventListener("resize", function() { _this.resize(); } );
29948 this.bodyEl.on('click', this.beforeSelectFile, this);
29951 this.bodyEl.on('touchstart', this.onTouchStart, this);
29952 this.bodyEl.on('touchmove', this.onTouchMove, this);
29953 this.bodyEl.on('touchend', this.onTouchEnd, this);
29957 this.bodyEl.on('mousedown', this.onMouseDown, this);
29958 this.bodyEl.on('mousemove', this.onMouseMove, this);
29959 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29960 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29961 Roo.get(document).on('mouseup', this.onMouseUp, this);
29964 this.selectorEl.on('change', this.onFileSelected, this);
29970 this.baseScale = 1;
29972 this.baseRotate = 1;
29973 this.dragable = false;
29974 this.pinching = false;
29977 this.cropData = false;
29978 this.notifyEl.dom.innerHTML = this.emptyText;
29980 this.selectorEl.dom.value = '';
29984 resize : function()
29986 if(this.fireEvent('resize', this) != false){
29987 this.setThumbBoxPosition();
29988 this.setCanvasPosition();
29992 onFooterButtonClick : function(e, el, o, type)
29995 case 'rotate-left' :
29996 this.onRotateLeft(e);
29998 case 'rotate-right' :
29999 this.onRotateRight(e);
30002 this.beforeSelectFile(e);
30017 this.fireEvent('footerbuttonclick', this, type);
30020 beforeSelectFile : function(e)
30022 e.preventDefault();
30024 if(this.fireEvent('beforeselectfile', this) != false){
30025 this.selectorEl.dom.click();
30029 onFileSelected : function(e)
30031 e.preventDefault();
30033 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30037 var file = this.selectorEl.dom.files[0];
30039 if(this.fireEvent('inspect', this, file) != false){
30040 this.prepare(file);
30045 trash : function(e)
30047 this.fireEvent('trash', this);
30050 download : function(e)
30052 this.fireEvent('download', this);
30055 loadCanvas : function(src)
30057 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30061 this.imageEl = document.createElement('img');
30065 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30067 this.imageEl.src = src;
30071 onLoadCanvas : function()
30073 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30074 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30076 this.bodyEl.un('click', this.beforeSelectFile, this);
30078 this.notifyEl.hide();
30079 this.thumbEl.show();
30080 this.footerEl.show();
30082 this.baseRotateLevel();
30084 if(this.isDocument){
30085 this.setThumbBoxSize();
30088 this.setThumbBoxPosition();
30090 this.baseScaleLevel();
30096 this.canvasLoaded = true;
30099 this.maskEl.unmask();
30104 setCanvasPosition : function()
30106 if(!this.canvasEl){
30110 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30111 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30113 this.previewEl.setLeft(pw);
30114 this.previewEl.setTop(ph);
30118 onMouseDown : function(e)
30122 this.dragable = true;
30123 this.pinching = false;
30125 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30126 this.dragable = false;
30130 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30131 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30135 onMouseMove : function(e)
30139 if(!this.canvasLoaded){
30143 if (!this.dragable){
30147 var minX = Math.ceil(this.thumbEl.getLeft(true));
30148 var minY = Math.ceil(this.thumbEl.getTop(true));
30150 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30151 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30153 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30154 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30156 x = x - this.mouseX;
30157 y = y - this.mouseY;
30159 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30160 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30162 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30163 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30165 this.previewEl.setLeft(bgX);
30166 this.previewEl.setTop(bgY);
30168 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30169 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30172 onMouseUp : function(e)
30176 this.dragable = false;
30179 onMouseWheel : function(e)
30183 this.startScale = this.scale;
30185 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30187 if(!this.zoomable()){
30188 this.scale = this.startScale;
30197 zoomable : function()
30199 var minScale = this.thumbEl.getWidth() / this.minWidth;
30201 if(this.minWidth < this.minHeight){
30202 minScale = this.thumbEl.getHeight() / this.minHeight;
30205 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30206 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30210 (this.rotate == 0 || this.rotate == 180) &&
30212 width > this.imageEl.OriginWidth ||
30213 height > this.imageEl.OriginHeight ||
30214 (width < this.minWidth && height < this.minHeight)
30222 (this.rotate == 90 || this.rotate == 270) &&
30224 width > this.imageEl.OriginWidth ||
30225 height > this.imageEl.OriginHeight ||
30226 (width < this.minHeight && height < this.minWidth)
30233 !this.isDocument &&
30234 (this.rotate == 0 || this.rotate == 180) &&
30236 width < this.minWidth ||
30237 width > this.imageEl.OriginWidth ||
30238 height < this.minHeight ||
30239 height > this.imageEl.OriginHeight
30246 !this.isDocument &&
30247 (this.rotate == 90 || this.rotate == 270) &&
30249 width < this.minHeight ||
30250 width > this.imageEl.OriginWidth ||
30251 height < this.minWidth ||
30252 height > this.imageEl.OriginHeight
30262 onRotateLeft : function(e)
30264 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30266 var minScale = this.thumbEl.getWidth() / this.minWidth;
30268 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30269 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30271 this.startScale = this.scale;
30273 while (this.getScaleLevel() < minScale){
30275 this.scale = this.scale + 1;
30277 if(!this.zoomable()){
30282 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30283 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30288 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30295 this.scale = this.startScale;
30297 this.onRotateFail();
30302 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30304 if(this.isDocument){
30305 this.setThumbBoxSize();
30306 this.setThumbBoxPosition();
30307 this.setCanvasPosition();
30312 this.fireEvent('rotate', this, 'left');
30316 onRotateRight : function(e)
30318 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30320 var minScale = this.thumbEl.getWidth() / this.minWidth;
30322 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30323 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30325 this.startScale = this.scale;
30327 while (this.getScaleLevel() < minScale){
30329 this.scale = this.scale + 1;
30331 if(!this.zoomable()){
30336 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30337 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30342 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30349 this.scale = this.startScale;
30351 this.onRotateFail();
30356 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30358 if(this.isDocument){
30359 this.setThumbBoxSize();
30360 this.setThumbBoxPosition();
30361 this.setCanvasPosition();
30366 this.fireEvent('rotate', this, 'right');
30369 onRotateFail : function()
30371 this.errorEl.show(true);
30375 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30380 this.previewEl.dom.innerHTML = '';
30382 var canvasEl = document.createElement("canvas");
30384 var contextEl = canvasEl.getContext("2d");
30386 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30387 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30388 var center = this.imageEl.OriginWidth / 2;
30390 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30391 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30392 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30393 center = this.imageEl.OriginHeight / 2;
30396 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30398 contextEl.translate(center, center);
30399 contextEl.rotate(this.rotate * Math.PI / 180);
30401 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30403 this.canvasEl = document.createElement("canvas");
30405 this.contextEl = this.canvasEl.getContext("2d");
30407 switch (this.rotate) {
30410 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30411 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30413 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30418 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30419 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30421 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30422 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);
30426 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30431 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30432 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30434 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30435 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);
30439 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);
30444 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30445 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30447 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30448 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30452 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);
30459 this.previewEl.appendChild(this.canvasEl);
30461 this.setCanvasPosition();
30466 if(!this.canvasLoaded){
30470 var imageCanvas = document.createElement("canvas");
30472 var imageContext = imageCanvas.getContext("2d");
30474 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30475 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30477 var center = imageCanvas.width / 2;
30479 imageContext.translate(center, center);
30481 imageContext.rotate(this.rotate * Math.PI / 180);
30483 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30485 var canvas = document.createElement("canvas");
30487 var context = canvas.getContext("2d");
30489 canvas.width = this.minWidth;
30490 canvas.height = this.minHeight;
30492 switch (this.rotate) {
30495 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30496 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30498 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30499 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30501 var targetWidth = this.minWidth - 2 * x;
30502 var targetHeight = this.minHeight - 2 * y;
30506 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30507 scale = targetWidth / width;
30510 if(x > 0 && y == 0){
30511 scale = targetHeight / height;
30514 if(x > 0 && y > 0){
30515 scale = targetWidth / width;
30517 if(width < height){
30518 scale = targetHeight / height;
30522 context.scale(scale, scale);
30524 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30525 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30527 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30528 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30530 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30535 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30536 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30538 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30539 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30541 var targetWidth = this.minWidth - 2 * x;
30542 var targetHeight = this.minHeight - 2 * y;
30546 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30547 scale = targetWidth / width;
30550 if(x > 0 && y == 0){
30551 scale = targetHeight / height;
30554 if(x > 0 && y > 0){
30555 scale = targetWidth / width;
30557 if(width < height){
30558 scale = targetHeight / height;
30562 context.scale(scale, scale);
30564 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30565 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30567 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30568 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30570 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30572 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30577 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30578 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30580 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30581 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30583 var targetWidth = this.minWidth - 2 * x;
30584 var targetHeight = this.minHeight - 2 * y;
30588 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30589 scale = targetWidth / width;
30592 if(x > 0 && y == 0){
30593 scale = targetHeight / height;
30596 if(x > 0 && y > 0){
30597 scale = targetWidth / width;
30599 if(width < height){
30600 scale = targetHeight / height;
30604 context.scale(scale, scale);
30606 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30607 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30609 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30610 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30612 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30613 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30615 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30620 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30621 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30623 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30624 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30626 var targetWidth = this.minWidth - 2 * x;
30627 var targetHeight = this.minHeight - 2 * y;
30631 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30632 scale = targetWidth / width;
30635 if(x > 0 && y == 0){
30636 scale = targetHeight / height;
30639 if(x > 0 && y > 0){
30640 scale = targetWidth / width;
30642 if(width < height){
30643 scale = targetHeight / height;
30647 context.scale(scale, scale);
30649 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30650 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30652 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30653 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30655 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30657 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30664 this.cropData = canvas.toDataURL(this.cropType);
30666 if(this.fireEvent('crop', this, this.cropData) !== false){
30667 this.process(this.file, this.cropData);
30674 setThumbBoxSize : function()
30678 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30679 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30680 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30682 this.minWidth = width;
30683 this.minHeight = height;
30685 if(this.rotate == 90 || this.rotate == 270){
30686 this.minWidth = height;
30687 this.minHeight = width;
30692 width = Math.ceil(this.minWidth * height / this.minHeight);
30694 if(this.minWidth > this.minHeight){
30696 height = Math.ceil(this.minHeight * width / this.minWidth);
30699 this.thumbEl.setStyle({
30700 width : width + 'px',
30701 height : height + 'px'
30708 setThumbBoxPosition : function()
30710 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30711 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30713 this.thumbEl.setLeft(x);
30714 this.thumbEl.setTop(y);
30718 baseRotateLevel : function()
30720 this.baseRotate = 1;
30723 typeof(this.exif) != 'undefined' &&
30724 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30725 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30727 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30730 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30734 baseScaleLevel : function()
30738 if(this.isDocument){
30740 if(this.baseRotate == 6 || this.baseRotate == 8){
30742 height = this.thumbEl.getHeight();
30743 this.baseScale = height / this.imageEl.OriginWidth;
30745 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30746 width = this.thumbEl.getWidth();
30747 this.baseScale = width / this.imageEl.OriginHeight;
30753 height = this.thumbEl.getHeight();
30754 this.baseScale = height / this.imageEl.OriginHeight;
30756 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30757 width = this.thumbEl.getWidth();
30758 this.baseScale = width / this.imageEl.OriginWidth;
30764 if(this.baseRotate == 6 || this.baseRotate == 8){
30766 width = this.thumbEl.getHeight();
30767 this.baseScale = width / this.imageEl.OriginHeight;
30769 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30770 height = this.thumbEl.getWidth();
30771 this.baseScale = height / this.imageEl.OriginHeight;
30774 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30775 height = this.thumbEl.getWidth();
30776 this.baseScale = height / this.imageEl.OriginHeight;
30778 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30779 width = this.thumbEl.getHeight();
30780 this.baseScale = width / this.imageEl.OriginWidth;
30787 width = this.thumbEl.getWidth();
30788 this.baseScale = width / this.imageEl.OriginWidth;
30790 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30791 height = this.thumbEl.getHeight();
30792 this.baseScale = height / this.imageEl.OriginHeight;
30795 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30797 height = this.thumbEl.getHeight();
30798 this.baseScale = height / this.imageEl.OriginHeight;
30800 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30801 width = this.thumbEl.getWidth();
30802 this.baseScale = width / this.imageEl.OriginWidth;
30810 getScaleLevel : function()
30812 return this.baseScale * Math.pow(1.1, this.scale);
30815 onTouchStart : function(e)
30817 if(!this.canvasLoaded){
30818 this.beforeSelectFile(e);
30822 var touches = e.browserEvent.touches;
30828 if(touches.length == 1){
30829 this.onMouseDown(e);
30833 if(touches.length != 2){
30839 for(var i = 0, finger; finger = touches[i]; i++){
30840 coords.push(finger.pageX, finger.pageY);
30843 var x = Math.pow(coords[0] - coords[2], 2);
30844 var y = Math.pow(coords[1] - coords[3], 2);
30846 this.startDistance = Math.sqrt(x + y);
30848 this.startScale = this.scale;
30850 this.pinching = true;
30851 this.dragable = false;
30855 onTouchMove : function(e)
30857 if(!this.pinching && !this.dragable){
30861 var touches = e.browserEvent.touches;
30868 this.onMouseMove(e);
30874 for(var i = 0, finger; finger = touches[i]; i++){
30875 coords.push(finger.pageX, finger.pageY);
30878 var x = Math.pow(coords[0] - coords[2], 2);
30879 var y = Math.pow(coords[1] - coords[3], 2);
30881 this.endDistance = Math.sqrt(x + y);
30883 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30885 if(!this.zoomable()){
30886 this.scale = this.startScale;
30894 onTouchEnd : function(e)
30896 this.pinching = false;
30897 this.dragable = false;
30901 process : function(file, crop)
30904 this.maskEl.mask(this.loadingText);
30907 this.xhr = new XMLHttpRequest();
30909 file.xhr = this.xhr;
30911 this.xhr.open(this.method, this.url, true);
30914 "Accept": "application/json",
30915 "Cache-Control": "no-cache",
30916 "X-Requested-With": "XMLHttpRequest"
30919 for (var headerName in headers) {
30920 var headerValue = headers[headerName];
30922 this.xhr.setRequestHeader(headerName, headerValue);
30928 this.xhr.onload = function()
30930 _this.xhrOnLoad(_this.xhr);
30933 this.xhr.onerror = function()
30935 _this.xhrOnError(_this.xhr);
30938 var formData = new FormData();
30940 formData.append('returnHTML', 'NO');
30943 formData.append('crop', crop);
30946 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30947 formData.append(this.paramName, file, file.name);
30950 if(typeof(file.filename) != 'undefined'){
30951 formData.append('filename', file.filename);
30954 if(typeof(file.mimetype) != 'undefined'){
30955 formData.append('mimetype', file.mimetype);
30958 if(this.fireEvent('arrange', this, formData) != false){
30959 this.xhr.send(formData);
30963 xhrOnLoad : function(xhr)
30966 this.maskEl.unmask();
30969 if (xhr.readyState !== 4) {
30970 this.fireEvent('exception', this, xhr);
30974 var response = Roo.decode(xhr.responseText);
30976 if(!response.success){
30977 this.fireEvent('exception', this, xhr);
30981 var response = Roo.decode(xhr.responseText);
30983 this.fireEvent('upload', this, response);
30987 xhrOnError : function()
30990 this.maskEl.unmask();
30993 Roo.log('xhr on error');
30995 var response = Roo.decode(xhr.responseText);
31001 prepare : function(file)
31004 this.maskEl.mask(this.loadingText);
31010 if(typeof(file) === 'string'){
31011 this.loadCanvas(file);
31015 if(!file || !this.urlAPI){
31020 this.cropType = file.type;
31024 if(this.fireEvent('prepare', this, this.file) != false){
31026 var reader = new FileReader();
31028 reader.onload = function (e) {
31029 if (e.target.error) {
31030 Roo.log(e.target.error);
31034 var buffer = e.target.result,
31035 dataView = new DataView(buffer),
31037 maxOffset = dataView.byteLength - 4,
31041 if (dataView.getUint16(0) === 0xffd8) {
31042 while (offset < maxOffset) {
31043 markerBytes = dataView.getUint16(offset);
31045 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31046 markerLength = dataView.getUint16(offset + 2) + 2;
31047 if (offset + markerLength > dataView.byteLength) {
31048 Roo.log('Invalid meta data: Invalid segment size.');
31052 if(markerBytes == 0xffe1){
31053 _this.parseExifData(
31060 offset += markerLength;
31070 var url = _this.urlAPI.createObjectURL(_this.file);
31072 _this.loadCanvas(url);
31077 reader.readAsArrayBuffer(this.file);
31083 parseExifData : function(dataView, offset, length)
31085 var tiffOffset = offset + 10,
31089 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31090 // No Exif data, might be XMP data instead
31094 // Check for the ASCII code for "Exif" (0x45786966):
31095 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31096 // No Exif data, might be XMP data instead
31099 if (tiffOffset + 8 > dataView.byteLength) {
31100 Roo.log('Invalid Exif data: Invalid segment size.');
31103 // Check for the two null bytes:
31104 if (dataView.getUint16(offset + 8) !== 0x0000) {
31105 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31108 // Check the byte alignment:
31109 switch (dataView.getUint16(tiffOffset)) {
31111 littleEndian = true;
31114 littleEndian = false;
31117 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31120 // Check for the TIFF tag marker (0x002A):
31121 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31122 Roo.log('Invalid Exif data: Missing TIFF marker.');
31125 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31126 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31128 this.parseExifTags(
31131 tiffOffset + dirOffset,
31136 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31141 if (dirOffset + 6 > dataView.byteLength) {
31142 Roo.log('Invalid Exif data: Invalid directory offset.');
31145 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31146 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31147 if (dirEndOffset + 4 > dataView.byteLength) {
31148 Roo.log('Invalid Exif data: Invalid directory size.');
31151 for (i = 0; i < tagsNumber; i += 1) {
31155 dirOffset + 2 + 12 * i, // tag offset
31159 // Return the offset to the next directory:
31160 return dataView.getUint32(dirEndOffset, littleEndian);
31163 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31165 var tag = dataView.getUint16(offset, littleEndian);
31167 this.exif[tag] = this.getExifValue(
31171 dataView.getUint16(offset + 2, littleEndian), // tag type
31172 dataView.getUint32(offset + 4, littleEndian), // tag length
31177 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31179 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31188 Roo.log('Invalid Exif data: Invalid tag type.');
31192 tagSize = tagType.size * length;
31193 // Determine if the value is contained in the dataOffset bytes,
31194 // or if the value at the dataOffset is a pointer to the actual data:
31195 dataOffset = tagSize > 4 ?
31196 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31197 if (dataOffset + tagSize > dataView.byteLength) {
31198 Roo.log('Invalid Exif data: Invalid data offset.');
31201 if (length === 1) {
31202 return tagType.getValue(dataView, dataOffset, littleEndian);
31205 for (i = 0; i < length; i += 1) {
31206 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31209 if (tagType.ascii) {
31211 // Concatenate the chars:
31212 for (i = 0; i < values.length; i += 1) {
31214 // Ignore the terminating NULL byte(s):
31215 if (c === '\u0000') {
31227 Roo.apply(Roo.bootstrap.UploadCropbox, {
31229 'Orientation': 0x0112
31233 1: 0, //'top-left',
31235 3: 180, //'bottom-right',
31236 // 4: 'bottom-left',
31238 6: 90, //'right-top',
31239 // 7: 'right-bottom',
31240 8: 270 //'left-bottom'
31244 // byte, 8-bit unsigned int:
31246 getValue: function (dataView, dataOffset) {
31247 return dataView.getUint8(dataOffset);
31251 // ascii, 8-bit byte:
31253 getValue: function (dataView, dataOffset) {
31254 return String.fromCharCode(dataView.getUint8(dataOffset));
31259 // short, 16 bit int:
31261 getValue: function (dataView, dataOffset, littleEndian) {
31262 return dataView.getUint16(dataOffset, littleEndian);
31266 // long, 32 bit int:
31268 getValue: function (dataView, dataOffset, littleEndian) {
31269 return dataView.getUint32(dataOffset, littleEndian);
31273 // rational = two long values, first is numerator, second is denominator:
31275 getValue: function (dataView, dataOffset, littleEndian) {
31276 return dataView.getUint32(dataOffset, littleEndian) /
31277 dataView.getUint32(dataOffset + 4, littleEndian);
31281 // slong, 32 bit signed int:
31283 getValue: function (dataView, dataOffset, littleEndian) {
31284 return dataView.getInt32(dataOffset, littleEndian);
31288 // srational, two slongs, first is numerator, second is denominator:
31290 getValue: function (dataView, dataOffset, littleEndian) {
31291 return dataView.getInt32(dataOffset, littleEndian) /
31292 dataView.getInt32(dataOffset + 4, littleEndian);
31302 cls : 'btn-group roo-upload-cropbox-rotate-left',
31303 action : 'rotate-left',
31307 cls : 'btn btn-default',
31308 html : '<i class="fa fa-undo"></i>'
31314 cls : 'btn-group roo-upload-cropbox-picture',
31315 action : 'picture',
31319 cls : 'btn btn-default',
31320 html : '<i class="fa fa-picture-o"></i>'
31326 cls : 'btn-group roo-upload-cropbox-rotate-right',
31327 action : 'rotate-right',
31331 cls : 'btn btn-default',
31332 html : '<i class="fa fa-repeat"></i>'
31340 cls : 'btn-group roo-upload-cropbox-rotate-left',
31341 action : 'rotate-left',
31345 cls : 'btn btn-default',
31346 html : '<i class="fa fa-undo"></i>'
31352 cls : 'btn-group roo-upload-cropbox-download',
31353 action : 'download',
31357 cls : 'btn btn-default',
31358 html : '<i class="fa fa-download"></i>'
31364 cls : 'btn-group roo-upload-cropbox-crop',
31369 cls : 'btn btn-default',
31370 html : '<i class="fa fa-crop"></i>'
31376 cls : 'btn-group roo-upload-cropbox-trash',
31381 cls : 'btn btn-default',
31382 html : '<i class="fa fa-trash"></i>'
31388 cls : 'btn-group roo-upload-cropbox-rotate-right',
31389 action : 'rotate-right',
31393 cls : 'btn btn-default',
31394 html : '<i class="fa fa-repeat"></i>'
31402 cls : 'btn-group roo-upload-cropbox-rotate-left',
31403 action : 'rotate-left',
31407 cls : 'btn btn-default',
31408 html : '<i class="fa fa-undo"></i>'
31414 cls : 'btn-group roo-upload-cropbox-rotate-right',
31415 action : 'rotate-right',
31419 cls : 'btn btn-default',
31420 html : '<i class="fa fa-repeat"></i>'
31433 * @class Roo.bootstrap.DocumentManager
31434 * @extends Roo.bootstrap.Component
31435 * Bootstrap DocumentManager class
31436 * @cfg {String} paramName default 'imageUpload'
31437 * @cfg {String} toolTipName default 'filename'
31438 * @cfg {String} method default POST
31439 * @cfg {String} url action url
31440 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31441 * @cfg {Boolean} multiple multiple upload default true
31442 * @cfg {Number} thumbSize default 300
31443 * @cfg {String} fieldLabel
31444 * @cfg {Number} labelWidth default 4
31445 * @cfg {String} labelAlign (left|top) default left
31446 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31447 * @cfg {Number} labellg set the width of label (1-12)
31448 * @cfg {Number} labelmd set the width of label (1-12)
31449 * @cfg {Number} labelsm set the width of label (1-12)
31450 * @cfg {Number} labelxs set the width of label (1-12)
31453 * Create a new DocumentManager
31454 * @param {Object} config The config object
31457 Roo.bootstrap.DocumentManager = function(config){
31458 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31461 this.delegates = [];
31466 * Fire when initial the DocumentManager
31467 * @param {Roo.bootstrap.DocumentManager} this
31472 * inspect selected file
31473 * @param {Roo.bootstrap.DocumentManager} this
31474 * @param {File} file
31479 * Fire when xhr load exception
31480 * @param {Roo.bootstrap.DocumentManager} this
31481 * @param {XMLHttpRequest} xhr
31483 "exception" : true,
31485 * @event afterupload
31486 * Fire when xhr load exception
31487 * @param {Roo.bootstrap.DocumentManager} this
31488 * @param {XMLHttpRequest} xhr
31490 "afterupload" : true,
31493 * prepare the form data
31494 * @param {Roo.bootstrap.DocumentManager} this
31495 * @param {Object} formData
31500 * Fire when remove the file
31501 * @param {Roo.bootstrap.DocumentManager} this
31502 * @param {Object} file
31507 * Fire after refresh the file
31508 * @param {Roo.bootstrap.DocumentManager} this
31513 * Fire after click the image
31514 * @param {Roo.bootstrap.DocumentManager} this
31515 * @param {Object} file
31520 * Fire when upload a image and editable set to true
31521 * @param {Roo.bootstrap.DocumentManager} this
31522 * @param {Object} file
31526 * @event beforeselectfile
31527 * Fire before select file
31528 * @param {Roo.bootstrap.DocumentManager} this
31530 "beforeselectfile" : true,
31533 * Fire before process file
31534 * @param {Roo.bootstrap.DocumentManager} this
31535 * @param {Object} file
31539 * @event previewrendered
31540 * Fire when preview rendered
31541 * @param {Roo.bootstrap.DocumentManager} this
31542 * @param {Object} file
31544 "previewrendered" : true,
31547 "previewResize" : true
31552 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31561 paramName : 'imageUpload',
31562 toolTipName : 'filename',
31565 labelAlign : 'left',
31575 getAutoCreate : function()
31577 var managerWidget = {
31579 cls : 'roo-document-manager',
31583 cls : 'roo-document-manager-selector',
31588 cls : 'roo-document-manager-uploader',
31592 cls : 'roo-document-manager-upload-btn',
31593 html : '<i class="fa fa-plus"></i>'
31604 cls : 'column col-md-12',
31609 if(this.fieldLabel.length){
31614 cls : 'column col-md-12',
31615 html : this.fieldLabel
31619 cls : 'column col-md-12',
31624 if(this.labelAlign == 'left'){
31629 html : this.fieldLabel
31638 if(this.labelWidth > 12){
31639 content[0].style = "width: " + this.labelWidth + 'px';
31642 if(this.labelWidth < 13 && this.labelmd == 0){
31643 this.labelmd = this.labelWidth;
31646 if(this.labellg > 0){
31647 content[0].cls += ' col-lg-' + this.labellg;
31648 content[1].cls += ' col-lg-' + (12 - this.labellg);
31651 if(this.labelmd > 0){
31652 content[0].cls += ' col-md-' + this.labelmd;
31653 content[1].cls += ' col-md-' + (12 - this.labelmd);
31656 if(this.labelsm > 0){
31657 content[0].cls += ' col-sm-' + this.labelsm;
31658 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31661 if(this.labelxs > 0){
31662 content[0].cls += ' col-xs-' + this.labelxs;
31663 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31671 cls : 'row clearfix',
31679 initEvents : function()
31681 this.managerEl = this.el.select('.roo-document-manager', true).first();
31682 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31684 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31685 this.selectorEl.hide();
31688 this.selectorEl.attr('multiple', 'multiple');
31691 this.selectorEl.on('change', this.onFileSelected, this);
31693 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31694 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31696 this.uploader.on('click', this.onUploaderClick, this);
31698 this.renderProgressDialog();
31702 window.addEventListener("resize", function() { _this.refresh(); } );
31704 this.fireEvent('initial', this);
31707 renderProgressDialog : function()
31711 this.progressDialog = new Roo.bootstrap.Modal({
31712 cls : 'roo-document-manager-progress-dialog',
31713 allow_close : false,
31724 btnclick : function() {
31725 _this.uploadCancel();
31731 this.progressDialog.render(Roo.get(document.body));
31733 this.progress = new Roo.bootstrap.Progress({
31734 cls : 'roo-document-manager-progress',
31739 this.progress.render(this.progressDialog.getChildContainer());
31741 this.progressBar = new Roo.bootstrap.ProgressBar({
31742 cls : 'roo-document-manager-progress-bar',
31745 aria_valuemax : 12,
31749 this.progressBar.render(this.progress.getChildContainer());
31752 onUploaderClick : function(e)
31754 e.preventDefault();
31756 if(this.fireEvent('beforeselectfile', this) != false){
31757 this.selectorEl.dom.click();
31762 onFileSelected : function(e)
31764 e.preventDefault();
31766 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31770 Roo.each(this.selectorEl.dom.files, function(file){
31771 if(this.fireEvent('inspect', this, file) != false){
31772 this.files.push(file);
31782 this.selectorEl.dom.value = '';
31784 if(!this.files || !this.files.length){
31788 if(this.boxes > 0 && this.files.length > this.boxes){
31789 this.files = this.files.slice(0, this.boxes);
31792 this.uploader.show();
31794 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31795 this.uploader.hide();
31804 Roo.each(this.files, function(file){
31806 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31807 var f = this.renderPreview(file);
31812 if(file.type.indexOf('image') != -1){
31813 this.delegates.push(
31815 _this.process(file);
31816 }).createDelegate(this)
31824 _this.process(file);
31825 }).createDelegate(this)
31830 this.files = files;
31832 this.delegates = this.delegates.concat(docs);
31834 if(!this.delegates.length){
31839 this.progressBar.aria_valuemax = this.delegates.length;
31846 arrange : function()
31848 if(!this.delegates.length){
31849 this.progressDialog.hide();
31854 var delegate = this.delegates.shift();
31856 this.progressDialog.show();
31858 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31860 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31865 refresh : function()
31867 this.uploader.show();
31869 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31870 this.uploader.hide();
31873 Roo.isTouch ? this.closable(false) : this.closable(true);
31875 this.fireEvent('refresh', this);
31878 onRemove : function(e, el, o)
31880 e.preventDefault();
31882 this.fireEvent('remove', this, o);
31886 remove : function(o)
31890 Roo.each(this.files, function(file){
31891 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31900 this.files = files;
31907 Roo.each(this.files, function(file){
31912 file.target.remove();
31921 onClick : function(e, el, o)
31923 e.preventDefault();
31925 this.fireEvent('click', this, o);
31929 closable : function(closable)
31931 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31933 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31945 xhrOnLoad : function(xhr)
31947 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31951 if (xhr.readyState !== 4) {
31953 this.fireEvent('exception', this, xhr);
31957 var response = Roo.decode(xhr.responseText);
31959 if(!response.success){
31961 this.fireEvent('exception', this, xhr);
31965 var file = this.renderPreview(response.data);
31967 this.files.push(file);
31971 this.fireEvent('afterupload', this, xhr);
31975 xhrOnError : function(xhr)
31977 Roo.log('xhr on error');
31979 var response = Roo.decode(xhr.responseText);
31986 process : function(file)
31988 if(this.fireEvent('process', this, file) !== false){
31989 if(this.editable && file.type.indexOf('image') != -1){
31990 this.fireEvent('edit', this, file);
31994 this.uploadStart(file, false);
32001 uploadStart : function(file, crop)
32003 this.xhr = new XMLHttpRequest();
32005 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32010 file.xhr = this.xhr;
32012 this.managerEl.createChild({
32014 cls : 'roo-document-manager-loading',
32018 tooltip : file.name,
32019 cls : 'roo-document-manager-thumb',
32020 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32026 this.xhr.open(this.method, this.url, true);
32029 "Accept": "application/json",
32030 "Cache-Control": "no-cache",
32031 "X-Requested-With": "XMLHttpRequest"
32034 for (var headerName in headers) {
32035 var headerValue = headers[headerName];
32037 this.xhr.setRequestHeader(headerName, headerValue);
32043 this.xhr.onload = function()
32045 _this.xhrOnLoad(_this.xhr);
32048 this.xhr.onerror = function()
32050 _this.xhrOnError(_this.xhr);
32053 var formData = new FormData();
32055 formData.append('returnHTML', 'NO');
32058 formData.append('crop', crop);
32061 formData.append(this.paramName, file, file.name);
32068 if(this.fireEvent('prepare', this, formData, options) != false){
32070 if(options.manually){
32074 this.xhr.send(formData);
32078 this.uploadCancel();
32081 uploadCancel : function()
32087 this.delegates = [];
32089 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32096 renderPreview : function(file)
32098 if(typeof(file.target) != 'undefined' && file.target){
32102 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32104 var previewEl = this.managerEl.createChild({
32106 cls : 'roo-document-manager-preview',
32110 tooltip : file[this.toolTipName],
32111 cls : 'roo-document-manager-thumb',
32112 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32117 html : '<i class="fa fa-times-circle"></i>'
32122 var close = previewEl.select('button.close', true).first();
32124 close.on('click', this.onRemove, this, file);
32126 file.target = previewEl;
32128 var image = previewEl.select('img', true).first();
32132 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32134 image.on('click', this.onClick, this, file);
32136 this.fireEvent('previewrendered', this, file);
32142 onPreviewLoad : function(file, image)
32144 if(typeof(file.target) == 'undefined' || !file.target){
32148 var width = image.dom.naturalWidth || image.dom.width;
32149 var height = image.dom.naturalHeight || image.dom.height;
32151 if(!this.previewResize) {
32155 if(width > height){
32156 file.target.addClass('wide');
32160 file.target.addClass('tall');
32165 uploadFromSource : function(file, crop)
32167 this.xhr = new XMLHttpRequest();
32169 this.managerEl.createChild({
32171 cls : 'roo-document-manager-loading',
32175 tooltip : file.name,
32176 cls : 'roo-document-manager-thumb',
32177 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32183 this.xhr.open(this.method, this.url, true);
32186 "Accept": "application/json",
32187 "Cache-Control": "no-cache",
32188 "X-Requested-With": "XMLHttpRequest"
32191 for (var headerName in headers) {
32192 var headerValue = headers[headerName];
32194 this.xhr.setRequestHeader(headerName, headerValue);
32200 this.xhr.onload = function()
32202 _this.xhrOnLoad(_this.xhr);
32205 this.xhr.onerror = function()
32207 _this.xhrOnError(_this.xhr);
32210 var formData = new FormData();
32212 formData.append('returnHTML', 'NO');
32214 formData.append('crop', crop);
32216 if(typeof(file.filename) != 'undefined'){
32217 formData.append('filename', file.filename);
32220 if(typeof(file.mimetype) != 'undefined'){
32221 formData.append('mimetype', file.mimetype);
32226 if(this.fireEvent('prepare', this, formData) != false){
32227 this.xhr.send(formData);
32237 * @class Roo.bootstrap.DocumentViewer
32238 * @extends Roo.bootstrap.Component
32239 * Bootstrap DocumentViewer class
32240 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32241 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32244 * Create a new DocumentViewer
32245 * @param {Object} config The config object
32248 Roo.bootstrap.DocumentViewer = function(config){
32249 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32254 * Fire after initEvent
32255 * @param {Roo.bootstrap.DocumentViewer} this
32261 * @param {Roo.bootstrap.DocumentViewer} this
32266 * Fire after download button
32267 * @param {Roo.bootstrap.DocumentViewer} this
32272 * Fire after trash button
32273 * @param {Roo.bootstrap.DocumentViewer} this
32280 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32282 showDownload : true,
32286 getAutoCreate : function()
32290 cls : 'roo-document-viewer',
32294 cls : 'roo-document-viewer-body',
32298 cls : 'roo-document-viewer-thumb',
32302 cls : 'roo-document-viewer-image'
32310 cls : 'roo-document-viewer-footer',
32313 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32317 cls : 'btn-group roo-document-viewer-download',
32321 cls : 'btn btn-default',
32322 html : '<i class="fa fa-download"></i>'
32328 cls : 'btn-group roo-document-viewer-trash',
32332 cls : 'btn btn-default',
32333 html : '<i class="fa fa-trash"></i>'
32346 initEvents : function()
32348 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32349 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32351 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32352 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32354 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32355 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32357 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32358 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32360 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32361 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32363 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32364 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32366 this.bodyEl.on('click', this.onClick, this);
32367 this.downloadBtn.on('click', this.onDownload, this);
32368 this.trashBtn.on('click', this.onTrash, this);
32370 this.downloadBtn.hide();
32371 this.trashBtn.hide();
32373 if(this.showDownload){
32374 this.downloadBtn.show();
32377 if(this.showTrash){
32378 this.trashBtn.show();
32381 if(!this.showDownload && !this.showTrash) {
32382 this.footerEl.hide();
32387 initial : function()
32389 this.fireEvent('initial', this);
32393 onClick : function(e)
32395 e.preventDefault();
32397 this.fireEvent('click', this);
32400 onDownload : function(e)
32402 e.preventDefault();
32404 this.fireEvent('download', this);
32407 onTrash : function(e)
32409 e.preventDefault();
32411 this.fireEvent('trash', this);
32423 * @class Roo.bootstrap.NavProgressBar
32424 * @extends Roo.bootstrap.Component
32425 * Bootstrap NavProgressBar class
32428 * Create a new nav progress bar
32429 * @param {Object} config The config object
32432 Roo.bootstrap.NavProgressBar = function(config){
32433 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32435 this.bullets = this.bullets || [];
32437 // Roo.bootstrap.NavProgressBar.register(this);
32441 * Fires when the active item changes
32442 * @param {Roo.bootstrap.NavProgressBar} this
32443 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32444 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32451 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32456 getAutoCreate : function()
32458 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32462 cls : 'roo-navigation-bar-group',
32466 cls : 'roo-navigation-top-bar'
32470 cls : 'roo-navigation-bullets-bar',
32474 cls : 'roo-navigation-bar'
32481 cls : 'roo-navigation-bottom-bar'
32491 initEvents: function()
32496 onRender : function(ct, position)
32498 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32500 if(this.bullets.length){
32501 Roo.each(this.bullets, function(b){
32510 addItem : function(cfg)
32512 var item = new Roo.bootstrap.NavProgressItem(cfg);
32514 item.parentId = this.id;
32515 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32518 var top = new Roo.bootstrap.Element({
32520 cls : 'roo-navigation-bar-text'
32523 var bottom = new Roo.bootstrap.Element({
32525 cls : 'roo-navigation-bar-text'
32528 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32529 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32531 var topText = new Roo.bootstrap.Element({
32533 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32536 var bottomText = new Roo.bootstrap.Element({
32538 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32541 topText.onRender(top.el, null);
32542 bottomText.onRender(bottom.el, null);
32545 item.bottomEl = bottom;
32548 this.barItems.push(item);
32553 getActive : function()
32555 var active = false;
32557 Roo.each(this.barItems, function(v){
32559 if (!v.isActive()) {
32571 setActiveItem : function(item)
32575 Roo.each(this.barItems, function(v){
32576 if (v.rid == item.rid) {
32580 if (v.isActive()) {
32581 v.setActive(false);
32586 item.setActive(true);
32588 this.fireEvent('changed', this, item, prev);
32591 getBarItem: function(rid)
32595 Roo.each(this.barItems, function(e) {
32596 if (e.rid != rid) {
32607 indexOfItem : function(item)
32611 Roo.each(this.barItems, function(v, i){
32613 if (v.rid != item.rid) {
32624 setActiveNext : function()
32626 var i = this.indexOfItem(this.getActive());
32628 if (i > this.barItems.length) {
32632 this.setActiveItem(this.barItems[i+1]);
32635 setActivePrev : function()
32637 var i = this.indexOfItem(this.getActive());
32643 this.setActiveItem(this.barItems[i-1]);
32646 format : function()
32648 if(!this.barItems.length){
32652 var width = 100 / this.barItems.length;
32654 Roo.each(this.barItems, function(i){
32655 i.el.setStyle('width', width + '%');
32656 i.topEl.el.setStyle('width', width + '%');
32657 i.bottomEl.el.setStyle('width', width + '%');
32666 * Nav Progress Item
32671 * @class Roo.bootstrap.NavProgressItem
32672 * @extends Roo.bootstrap.Component
32673 * Bootstrap NavProgressItem class
32674 * @cfg {String} rid the reference id
32675 * @cfg {Boolean} active (true|false) Is item active default false
32676 * @cfg {Boolean} disabled (true|false) Is item active default false
32677 * @cfg {String} html
32678 * @cfg {String} position (top|bottom) text position default bottom
32679 * @cfg {String} icon show icon instead of number
32682 * Create a new NavProgressItem
32683 * @param {Object} config The config object
32685 Roo.bootstrap.NavProgressItem = function(config){
32686 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32691 * The raw click event for the entire grid.
32692 * @param {Roo.bootstrap.NavProgressItem} this
32693 * @param {Roo.EventObject} e
32700 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32706 position : 'bottom',
32709 getAutoCreate : function()
32711 var iconCls = 'roo-navigation-bar-item-icon';
32713 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32717 cls: 'roo-navigation-bar-item',
32727 cfg.cls += ' active';
32730 cfg.cls += ' disabled';
32736 disable : function()
32738 this.setDisabled(true);
32741 enable : function()
32743 this.setDisabled(false);
32746 initEvents: function()
32748 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32750 this.iconEl.on('click', this.onClick, this);
32753 onClick : function(e)
32755 e.preventDefault();
32761 if(this.fireEvent('click', this, e) === false){
32765 this.parent().setActiveItem(this);
32768 isActive: function ()
32770 return this.active;
32773 setActive : function(state)
32775 if(this.active == state){
32779 this.active = state;
32782 this.el.addClass('active');
32786 this.el.removeClass('active');
32791 setDisabled : function(state)
32793 if(this.disabled == state){
32797 this.disabled = state;
32800 this.el.addClass('disabled');
32804 this.el.removeClass('disabled');
32807 tooltipEl : function()
32809 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32822 * @class Roo.bootstrap.FieldLabel
32823 * @extends Roo.bootstrap.Component
32824 * Bootstrap FieldLabel class
32825 * @cfg {String} html contents of the element
32826 * @cfg {String} tag tag of the element default label
32827 * @cfg {String} cls class of the element
32828 * @cfg {String} target label target
32829 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32830 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32831 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32832 * @cfg {String} iconTooltip default "This field is required"
32833 * @cfg {String} indicatorpos (left|right) default left
32836 * Create a new FieldLabel
32837 * @param {Object} config The config object
32840 Roo.bootstrap.FieldLabel = function(config){
32841 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32846 * Fires after the field has been marked as invalid.
32847 * @param {Roo.form.FieldLabel} this
32848 * @param {String} msg The validation message
32853 * Fires after the field has been validated with no errors.
32854 * @param {Roo.form.FieldLabel} this
32860 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32867 invalidClass : 'has-warning',
32868 validClass : 'has-success',
32869 iconTooltip : 'This field is required',
32870 indicatorpos : 'left',
32872 getAutoCreate : function(){
32875 if (!this.allowBlank) {
32881 cls : 'roo-bootstrap-field-label ' + this.cls,
32886 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32887 tooltip : this.iconTooltip
32896 if(this.indicatorpos == 'right'){
32899 cls : 'roo-bootstrap-field-label ' + this.cls,
32908 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32909 tooltip : this.iconTooltip
32918 initEvents: function()
32920 Roo.bootstrap.Element.superclass.initEvents.call(this);
32922 this.indicator = this.indicatorEl();
32924 if(this.indicator){
32925 this.indicator.removeClass('visible');
32926 this.indicator.addClass('invisible');
32929 Roo.bootstrap.FieldLabel.register(this);
32932 indicatorEl : function()
32934 var indicator = this.el.select('i.roo-required-indicator',true).first();
32945 * Mark this field as valid
32947 markValid : function()
32949 if(this.indicator){
32950 this.indicator.removeClass('visible');
32951 this.indicator.addClass('invisible');
32953 if (Roo.bootstrap.version == 3) {
32954 this.el.removeClass(this.invalidClass);
32955 this.el.addClass(this.validClass);
32957 this.el.removeClass('is-invalid');
32958 this.el.addClass('is-valid');
32962 this.fireEvent('valid', this);
32966 * Mark this field as invalid
32967 * @param {String} msg The validation message
32969 markInvalid : function(msg)
32971 if(this.indicator){
32972 this.indicator.removeClass('invisible');
32973 this.indicator.addClass('visible');
32975 if (Roo.bootstrap.version == 3) {
32976 this.el.removeClass(this.validClass);
32977 this.el.addClass(this.invalidClass);
32979 this.el.removeClass('is-valid');
32980 this.el.addClass('is-invalid');
32984 this.fireEvent('invalid', this, msg);
32990 Roo.apply(Roo.bootstrap.FieldLabel, {
32995 * register a FieldLabel Group
32996 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32998 register : function(label)
33000 if(this.groups.hasOwnProperty(label.target)){
33004 this.groups[label.target] = label;
33008 * fetch a FieldLabel Group based on the target
33009 * @param {string} target
33010 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33012 get: function(target) {
33013 if (typeof(this.groups[target]) == 'undefined') {
33017 return this.groups[target] ;
33026 * page DateSplitField.
33032 * @class Roo.bootstrap.DateSplitField
33033 * @extends Roo.bootstrap.Component
33034 * Bootstrap DateSplitField class
33035 * @cfg {string} fieldLabel - the label associated
33036 * @cfg {Number} labelWidth set the width of label (0-12)
33037 * @cfg {String} labelAlign (top|left)
33038 * @cfg {Boolean} dayAllowBlank (true|false) default false
33039 * @cfg {Boolean} monthAllowBlank (true|false) default false
33040 * @cfg {Boolean} yearAllowBlank (true|false) default false
33041 * @cfg {string} dayPlaceholder
33042 * @cfg {string} monthPlaceholder
33043 * @cfg {string} yearPlaceholder
33044 * @cfg {string} dayFormat default 'd'
33045 * @cfg {string} monthFormat default 'm'
33046 * @cfg {string} yearFormat default 'Y'
33047 * @cfg {Number} labellg set the width of label (1-12)
33048 * @cfg {Number} labelmd set the width of label (1-12)
33049 * @cfg {Number} labelsm set the width of label (1-12)
33050 * @cfg {Number} labelxs set the width of label (1-12)
33054 * Create a new DateSplitField
33055 * @param {Object} config The config object
33058 Roo.bootstrap.DateSplitField = function(config){
33059 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33065 * getting the data of years
33066 * @param {Roo.bootstrap.DateSplitField} this
33067 * @param {Object} years
33072 * getting the data of days
33073 * @param {Roo.bootstrap.DateSplitField} this
33074 * @param {Object} days
33079 * Fires after the field has been marked as invalid.
33080 * @param {Roo.form.Field} this
33081 * @param {String} msg The validation message
33086 * Fires after the field has been validated with no errors.
33087 * @param {Roo.form.Field} this
33093 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33096 labelAlign : 'top',
33098 dayAllowBlank : false,
33099 monthAllowBlank : false,
33100 yearAllowBlank : false,
33101 dayPlaceholder : '',
33102 monthPlaceholder : '',
33103 yearPlaceholder : '',
33107 isFormField : true,
33113 getAutoCreate : function()
33117 cls : 'row roo-date-split-field-group',
33122 cls : 'form-hidden-field roo-date-split-field-group-value',
33128 var labelCls = 'col-md-12';
33129 var contentCls = 'col-md-4';
33131 if(this.fieldLabel){
33135 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33139 html : this.fieldLabel
33144 if(this.labelAlign == 'left'){
33146 if(this.labelWidth > 12){
33147 label.style = "width: " + this.labelWidth + 'px';
33150 if(this.labelWidth < 13 && this.labelmd == 0){
33151 this.labelmd = this.labelWidth;
33154 if(this.labellg > 0){
33155 labelCls = ' col-lg-' + this.labellg;
33156 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33159 if(this.labelmd > 0){
33160 labelCls = ' col-md-' + this.labelmd;
33161 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33164 if(this.labelsm > 0){
33165 labelCls = ' col-sm-' + this.labelsm;
33166 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33169 if(this.labelxs > 0){
33170 labelCls = ' col-xs-' + this.labelxs;
33171 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33175 label.cls += ' ' + labelCls;
33177 cfg.cn.push(label);
33180 Roo.each(['day', 'month', 'year'], function(t){
33183 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33190 inputEl: function ()
33192 return this.el.select('.roo-date-split-field-group-value', true).first();
33195 onRender : function(ct, position)
33199 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33201 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33203 this.dayField = new Roo.bootstrap.ComboBox({
33204 allowBlank : this.dayAllowBlank,
33205 alwaysQuery : true,
33206 displayField : 'value',
33209 forceSelection : true,
33211 placeholder : this.dayPlaceholder,
33212 selectOnFocus : true,
33213 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33214 triggerAction : 'all',
33216 valueField : 'value',
33217 store : new Roo.data.SimpleStore({
33218 data : (function() {
33220 _this.fireEvent('days', _this, days);
33223 fields : [ 'value' ]
33226 select : function (_self, record, index)
33228 _this.setValue(_this.getValue());
33233 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33235 this.monthField = new Roo.bootstrap.MonthField({
33236 after : '<i class=\"fa fa-calendar\"></i>',
33237 allowBlank : this.monthAllowBlank,
33238 placeholder : this.monthPlaceholder,
33241 render : function (_self)
33243 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33244 e.preventDefault();
33248 select : function (_self, oldvalue, newvalue)
33250 _this.setValue(_this.getValue());
33255 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33257 this.yearField = new Roo.bootstrap.ComboBox({
33258 allowBlank : this.yearAllowBlank,
33259 alwaysQuery : true,
33260 displayField : 'value',
33263 forceSelection : true,
33265 placeholder : this.yearPlaceholder,
33266 selectOnFocus : true,
33267 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33268 triggerAction : 'all',
33270 valueField : 'value',
33271 store : new Roo.data.SimpleStore({
33272 data : (function() {
33274 _this.fireEvent('years', _this, years);
33277 fields : [ 'value' ]
33280 select : function (_self, record, index)
33282 _this.setValue(_this.getValue());
33287 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33290 setValue : function(v, format)
33292 this.inputEl.dom.value = v;
33294 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33296 var d = Date.parseDate(v, f);
33303 this.setDay(d.format(this.dayFormat));
33304 this.setMonth(d.format(this.monthFormat));
33305 this.setYear(d.format(this.yearFormat));
33312 setDay : function(v)
33314 this.dayField.setValue(v);
33315 this.inputEl.dom.value = this.getValue();
33320 setMonth : function(v)
33322 this.monthField.setValue(v, true);
33323 this.inputEl.dom.value = this.getValue();
33328 setYear : function(v)
33330 this.yearField.setValue(v);
33331 this.inputEl.dom.value = this.getValue();
33336 getDay : function()
33338 return this.dayField.getValue();
33341 getMonth : function()
33343 return this.monthField.getValue();
33346 getYear : function()
33348 return this.yearField.getValue();
33351 getValue : function()
33353 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33355 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33365 this.inputEl.dom.value = '';
33370 validate : function()
33372 var d = this.dayField.validate();
33373 var m = this.monthField.validate();
33374 var y = this.yearField.validate();
33379 (!this.dayAllowBlank && !d) ||
33380 (!this.monthAllowBlank && !m) ||
33381 (!this.yearAllowBlank && !y)
33386 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33395 this.markInvalid();
33400 markValid : function()
33403 var label = this.el.select('label', true).first();
33404 var icon = this.el.select('i.fa-star', true).first();
33410 this.fireEvent('valid', this);
33414 * Mark this field as invalid
33415 * @param {String} msg The validation message
33417 markInvalid : function(msg)
33420 var label = this.el.select('label', true).first();
33421 var icon = this.el.select('i.fa-star', true).first();
33423 if(label && !icon){
33424 this.el.select('.roo-date-split-field-label', true).createChild({
33426 cls : 'text-danger fa fa-lg fa-star',
33427 tooltip : 'This field is required',
33428 style : 'margin-right:5px;'
33432 this.fireEvent('invalid', this, msg);
33435 clearInvalid : function()
33437 var label = this.el.select('label', true).first();
33438 var icon = this.el.select('i.fa-star', true).first();
33444 this.fireEvent('valid', this);
33447 getName: function()
33457 * http://masonry.desandro.com
33459 * The idea is to render all the bricks based on vertical width...
33461 * The original code extends 'outlayer' - we might need to use that....
33467 * @class Roo.bootstrap.LayoutMasonry
33468 * @extends Roo.bootstrap.Component
33469 * Bootstrap Layout Masonry class
33472 * Create a new Element
33473 * @param {Object} config The config object
33476 Roo.bootstrap.LayoutMasonry = function(config){
33478 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33482 Roo.bootstrap.LayoutMasonry.register(this);
33488 * Fire after layout the items
33489 * @param {Roo.bootstrap.LayoutMasonry} this
33490 * @param {Roo.EventObject} e
33497 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33500 * @cfg {Boolean} isLayoutInstant = no animation?
33502 isLayoutInstant : false, // needed?
33505 * @cfg {Number} boxWidth width of the columns
33510 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33515 * @cfg {Number} padWidth padding below box..
33520 * @cfg {Number} gutter gutter width..
33525 * @cfg {Number} maxCols maximum number of columns
33531 * @cfg {Boolean} isAutoInitial defalut true
33533 isAutoInitial : true,
33538 * @cfg {Boolean} isHorizontal defalut false
33540 isHorizontal : false,
33542 currentSize : null,
33548 bricks: null, //CompositeElement
33552 _isLayoutInited : false,
33554 // isAlternative : false, // only use for vertical layout...
33557 * @cfg {Number} alternativePadWidth padding below box..
33559 alternativePadWidth : 50,
33561 selectedBrick : [],
33563 getAutoCreate : function(){
33565 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33569 cls: 'blog-masonary-wrapper ' + this.cls,
33571 cls : 'mas-boxes masonary'
33578 getChildContainer: function( )
33580 if (this.boxesEl) {
33581 return this.boxesEl;
33584 this.boxesEl = this.el.select('.mas-boxes').first();
33586 return this.boxesEl;
33590 initEvents : function()
33594 if(this.isAutoInitial){
33595 Roo.log('hook children rendered');
33596 this.on('childrenrendered', function() {
33597 Roo.log('children rendered');
33603 initial : function()
33605 this.selectedBrick = [];
33607 this.currentSize = this.el.getBox(true);
33609 Roo.EventManager.onWindowResize(this.resize, this);
33611 if(!this.isAutoInitial){
33619 //this.layout.defer(500,this);
33623 resize : function()
33625 var cs = this.el.getBox(true);
33628 this.currentSize.width == cs.width &&
33629 this.currentSize.x == cs.x &&
33630 this.currentSize.height == cs.height &&
33631 this.currentSize.y == cs.y
33633 Roo.log("no change in with or X or Y");
33637 this.currentSize = cs;
33643 layout : function()
33645 this._resetLayout();
33647 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33649 this.layoutItems( isInstant );
33651 this._isLayoutInited = true;
33653 this.fireEvent('layout', this);
33657 _resetLayout : function()
33659 if(this.isHorizontal){
33660 this.horizontalMeasureColumns();
33664 this.verticalMeasureColumns();
33668 verticalMeasureColumns : function()
33670 this.getContainerWidth();
33672 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33673 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33677 var boxWidth = this.boxWidth + this.padWidth;
33679 if(this.containerWidth < this.boxWidth){
33680 boxWidth = this.containerWidth
33683 var containerWidth = this.containerWidth;
33685 var cols = Math.floor(containerWidth / boxWidth);
33687 this.cols = Math.max( cols, 1 );
33689 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33691 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33693 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33695 this.colWidth = boxWidth + avail - this.padWidth;
33697 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33698 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33701 horizontalMeasureColumns : function()
33703 this.getContainerWidth();
33705 var boxWidth = this.boxWidth;
33707 if(this.containerWidth < boxWidth){
33708 boxWidth = this.containerWidth;
33711 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33713 this.el.setHeight(boxWidth);
33717 getContainerWidth : function()
33719 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33722 layoutItems : function( isInstant )
33724 Roo.log(this.bricks);
33726 var items = Roo.apply([], this.bricks);
33728 if(this.isHorizontal){
33729 this._horizontalLayoutItems( items , isInstant );
33733 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33734 // this._verticalAlternativeLayoutItems( items , isInstant );
33738 this._verticalLayoutItems( items , isInstant );
33742 _verticalLayoutItems : function ( items , isInstant)
33744 if ( !items || !items.length ) {
33749 ['xs', 'xs', 'xs', 'tall'],
33750 ['xs', 'xs', 'tall'],
33751 ['xs', 'xs', 'sm'],
33752 ['xs', 'xs', 'xs'],
33758 ['sm', 'xs', 'xs'],
33762 ['tall', 'xs', 'xs', 'xs'],
33763 ['tall', 'xs', 'xs'],
33775 Roo.each(items, function(item, k){
33777 switch (item.size) {
33778 // these layouts take up a full box,
33789 boxes.push([item]);
33812 var filterPattern = function(box, length)
33820 var pattern = box.slice(0, length);
33824 Roo.each(pattern, function(i){
33825 format.push(i.size);
33828 Roo.each(standard, function(s){
33830 if(String(s) != String(format)){
33839 if(!match && length == 1){
33844 filterPattern(box, length - 1);
33848 queue.push(pattern);
33850 box = box.slice(length, box.length);
33852 filterPattern(box, 4);
33858 Roo.each(boxes, function(box, k){
33864 if(box.length == 1){
33869 filterPattern(box, 4);
33873 this._processVerticalLayoutQueue( queue, isInstant );
33877 // _verticalAlternativeLayoutItems : function( items , isInstant )
33879 // if ( !items || !items.length ) {
33883 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33887 _horizontalLayoutItems : function ( items , isInstant)
33889 if ( !items || !items.length || items.length < 3) {
33895 var eItems = items.slice(0, 3);
33897 items = items.slice(3, items.length);
33900 ['xs', 'xs', 'xs', 'wide'],
33901 ['xs', 'xs', 'wide'],
33902 ['xs', 'xs', 'sm'],
33903 ['xs', 'xs', 'xs'],
33909 ['sm', 'xs', 'xs'],
33913 ['wide', 'xs', 'xs', 'xs'],
33914 ['wide', 'xs', 'xs'],
33927 Roo.each(items, function(item, k){
33929 switch (item.size) {
33940 boxes.push([item]);
33964 var filterPattern = function(box, length)
33972 var pattern = box.slice(0, length);
33976 Roo.each(pattern, function(i){
33977 format.push(i.size);
33980 Roo.each(standard, function(s){
33982 if(String(s) != String(format)){
33991 if(!match && length == 1){
33996 filterPattern(box, length - 1);
34000 queue.push(pattern);
34002 box = box.slice(length, box.length);
34004 filterPattern(box, 4);
34010 Roo.each(boxes, function(box, k){
34016 if(box.length == 1){
34021 filterPattern(box, 4);
34028 var pos = this.el.getBox(true);
34032 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34034 var hit_end = false;
34036 Roo.each(queue, function(box){
34040 Roo.each(box, function(b){
34042 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34052 Roo.each(box, function(b){
34054 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34057 mx = Math.max(mx, b.x);
34061 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34065 Roo.each(box, function(b){
34067 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34081 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34084 /** Sets position of item in DOM
34085 * @param {Element} item
34086 * @param {Number} x - horizontal position
34087 * @param {Number} y - vertical position
34088 * @param {Boolean} isInstant - disables transitions
34090 _processVerticalLayoutQueue : function( queue, isInstant )
34092 var pos = this.el.getBox(true);
34097 for (var i = 0; i < this.cols; i++){
34101 Roo.each(queue, function(box, k){
34103 var col = k % this.cols;
34105 Roo.each(box, function(b,kk){
34107 b.el.position('absolute');
34109 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34110 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34112 if(b.size == 'md-left' || b.size == 'md-right'){
34113 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34114 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34117 b.el.setWidth(width);
34118 b.el.setHeight(height);
34120 b.el.select('iframe',true).setSize(width,height);
34124 for (var i = 0; i < this.cols; i++){
34126 if(maxY[i] < maxY[col]){
34131 col = Math.min(col, i);
34135 x = pos.x + col * (this.colWidth + this.padWidth);
34139 var positions = [];
34141 switch (box.length){
34143 positions = this.getVerticalOneBoxColPositions(x, y, box);
34146 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34149 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34152 positions = this.getVerticalFourBoxColPositions(x, y, box);
34158 Roo.each(box, function(b,kk){
34160 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34162 var sz = b.el.getSize();
34164 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34172 for (var i = 0; i < this.cols; i++){
34173 mY = Math.max(mY, maxY[i]);
34176 this.el.setHeight(mY - pos.y);
34180 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34182 // var pos = this.el.getBox(true);
34185 // var maxX = pos.right;
34187 // var maxHeight = 0;
34189 // Roo.each(items, function(item, k){
34193 // item.el.position('absolute');
34195 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34197 // item.el.setWidth(width);
34199 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34201 // item.el.setHeight(height);
34204 // item.el.setXY([x, y], isInstant ? false : true);
34206 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34209 // y = y + height + this.alternativePadWidth;
34211 // maxHeight = maxHeight + height + this.alternativePadWidth;
34215 // this.el.setHeight(maxHeight);
34219 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34221 var pos = this.el.getBox(true);
34226 var maxX = pos.right;
34228 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34230 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34232 Roo.each(queue, function(box, k){
34234 Roo.each(box, function(b, kk){
34236 b.el.position('absolute');
34238 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34239 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34241 if(b.size == 'md-left' || b.size == 'md-right'){
34242 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34243 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34246 b.el.setWidth(width);
34247 b.el.setHeight(height);
34255 var positions = [];
34257 switch (box.length){
34259 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34262 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34265 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34268 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34274 Roo.each(box, function(b,kk){
34276 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34278 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34286 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34288 Roo.each(eItems, function(b,k){
34290 b.size = (k == 0) ? 'sm' : 'xs';
34291 b.x = (k == 0) ? 2 : 1;
34292 b.y = (k == 0) ? 2 : 1;
34294 b.el.position('absolute');
34296 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34298 b.el.setWidth(width);
34300 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34302 b.el.setHeight(height);
34306 var positions = [];
34309 x : maxX - this.unitWidth * 2 - this.gutter,
34314 x : maxX - this.unitWidth,
34315 y : minY + (this.unitWidth + this.gutter) * 2
34319 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34323 Roo.each(eItems, function(b,k){
34325 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34331 getVerticalOneBoxColPositions : function(x, y, box)
34335 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34337 if(box[0].size == 'md-left'){
34341 if(box[0].size == 'md-right'){
34346 x : x + (this.unitWidth + this.gutter) * rand,
34353 getVerticalTwoBoxColPositions : function(x, y, box)
34357 if(box[0].size == 'xs'){
34361 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34365 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34379 x : x + (this.unitWidth + this.gutter) * 2,
34380 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34387 getVerticalThreeBoxColPositions : function(x, y, box)
34391 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34399 x : x + (this.unitWidth + this.gutter) * 1,
34404 x : x + (this.unitWidth + this.gutter) * 2,
34412 if(box[0].size == 'xs' && box[1].size == 'xs'){
34421 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34425 x : x + (this.unitWidth + this.gutter) * 1,
34439 x : x + (this.unitWidth + this.gutter) * 2,
34444 x : x + (this.unitWidth + this.gutter) * 2,
34445 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34452 getVerticalFourBoxColPositions : function(x, y, box)
34456 if(box[0].size == 'xs'){
34465 y : y + (this.unitHeight + this.gutter) * 1
34470 y : y + (this.unitHeight + this.gutter) * 2
34474 x : x + (this.unitWidth + this.gutter) * 1,
34488 x : x + (this.unitWidth + this.gutter) * 2,
34493 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34494 y : y + (this.unitHeight + this.gutter) * 1
34498 x : x + (this.unitWidth + this.gutter) * 2,
34499 y : y + (this.unitWidth + this.gutter) * 2
34506 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34510 if(box[0].size == 'md-left'){
34512 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34519 if(box[0].size == 'md-right'){
34521 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34522 y : minY + (this.unitWidth + this.gutter) * 1
34528 var rand = Math.floor(Math.random() * (4 - box[0].y));
34531 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34532 y : minY + (this.unitWidth + this.gutter) * rand
34539 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34543 if(box[0].size == 'xs'){
34546 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34551 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34552 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34560 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34565 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34566 y : minY + (this.unitWidth + this.gutter) * 2
34573 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34577 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34580 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34585 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34586 y : minY + (this.unitWidth + this.gutter) * 1
34590 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34591 y : minY + (this.unitWidth + this.gutter) * 2
34598 if(box[0].size == 'xs' && box[1].size == 'xs'){
34601 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34606 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34611 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34612 y : minY + (this.unitWidth + this.gutter) * 1
34620 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34625 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34626 y : minY + (this.unitWidth + this.gutter) * 2
34630 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34631 y : minY + (this.unitWidth + this.gutter) * 2
34638 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34642 if(box[0].size == 'xs'){
34645 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34650 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34655 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),
34660 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34661 y : minY + (this.unitWidth + this.gutter) * 1
34669 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34674 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34675 y : minY + (this.unitWidth + this.gutter) * 2
34679 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34680 y : minY + (this.unitWidth + this.gutter) * 2
34684 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),
34685 y : minY + (this.unitWidth + this.gutter) * 2
34693 * remove a Masonry Brick
34694 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34696 removeBrick : function(brick_id)
34702 for (var i = 0; i<this.bricks.length; i++) {
34703 if (this.bricks[i].id == brick_id) {
34704 this.bricks.splice(i,1);
34705 this.el.dom.removeChild(Roo.get(brick_id).dom);
34712 * adds a Masonry Brick
34713 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34715 addBrick : function(cfg)
34717 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34718 //this.register(cn);
34719 cn.parentId = this.id;
34720 cn.render(this.el);
34725 * register a Masonry Brick
34726 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34729 register : function(brick)
34731 this.bricks.push(brick);
34732 brick.masonryId = this.id;
34736 * clear all the Masonry Brick
34738 clearAll : function()
34741 //this.getChildContainer().dom.innerHTML = "";
34742 this.el.dom.innerHTML = '';
34745 getSelected : function()
34747 if (!this.selectedBrick) {
34751 return this.selectedBrick;
34755 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34759 * register a Masonry Layout
34760 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34763 register : function(layout)
34765 this.groups[layout.id] = layout;
34768 * fetch a Masonry Layout based on the masonry layout ID
34769 * @param {string} the masonry layout to add
34770 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34773 get: function(layout_id) {
34774 if (typeof(this.groups[layout_id]) == 'undefined') {
34777 return this.groups[layout_id] ;
34789 * http://masonry.desandro.com
34791 * The idea is to render all the bricks based on vertical width...
34793 * The original code extends 'outlayer' - we might need to use that....
34799 * @class Roo.bootstrap.LayoutMasonryAuto
34800 * @extends Roo.bootstrap.Component
34801 * Bootstrap Layout Masonry class
34804 * Create a new Element
34805 * @param {Object} config The config object
34808 Roo.bootstrap.LayoutMasonryAuto = function(config){
34809 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34812 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34815 * @cfg {Boolean} isFitWidth - resize the width..
34817 isFitWidth : false, // options..
34819 * @cfg {Boolean} isOriginLeft = left align?
34821 isOriginLeft : true,
34823 * @cfg {Boolean} isOriginTop = top align?
34825 isOriginTop : false,
34827 * @cfg {Boolean} isLayoutInstant = no animation?
34829 isLayoutInstant : false, // needed?
34831 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34833 isResizingContainer : true,
34835 * @cfg {Number} columnWidth width of the columns
34841 * @cfg {Number} maxCols maximum number of columns
34846 * @cfg {Number} padHeight padding below box..
34852 * @cfg {Boolean} isAutoInitial defalut true
34855 isAutoInitial : true,
34861 initialColumnWidth : 0,
34862 currentSize : null,
34864 colYs : null, // array.
34871 bricks: null, //CompositeElement
34872 cols : 0, // array?
34873 // element : null, // wrapped now this.el
34874 _isLayoutInited : null,
34877 getAutoCreate : function(){
34881 cls: 'blog-masonary-wrapper ' + this.cls,
34883 cls : 'mas-boxes masonary'
34890 getChildContainer: function( )
34892 if (this.boxesEl) {
34893 return this.boxesEl;
34896 this.boxesEl = this.el.select('.mas-boxes').first();
34898 return this.boxesEl;
34902 initEvents : function()
34906 if(this.isAutoInitial){
34907 Roo.log('hook children rendered');
34908 this.on('childrenrendered', function() {
34909 Roo.log('children rendered');
34916 initial : function()
34918 this.reloadItems();
34920 this.currentSize = this.el.getBox(true);
34922 /// was window resize... - let's see if this works..
34923 Roo.EventManager.onWindowResize(this.resize, this);
34925 if(!this.isAutoInitial){
34930 this.layout.defer(500,this);
34933 reloadItems: function()
34935 this.bricks = this.el.select('.masonry-brick', true);
34937 this.bricks.each(function(b) {
34938 //Roo.log(b.getSize());
34939 if (!b.attr('originalwidth')) {
34940 b.attr('originalwidth', b.getSize().width);
34945 Roo.log(this.bricks.elements.length);
34948 resize : function()
34951 var cs = this.el.getBox(true);
34953 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34954 Roo.log("no change in with or X");
34957 this.currentSize = cs;
34961 layout : function()
34964 this._resetLayout();
34965 //this._manageStamps();
34967 // don't animate first layout
34968 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34969 this.layoutItems( isInstant );
34971 // flag for initalized
34972 this._isLayoutInited = true;
34975 layoutItems : function( isInstant )
34977 //var items = this._getItemsForLayout( this.items );
34978 // original code supports filtering layout items.. we just ignore it..
34980 this._layoutItems( this.bricks , isInstant );
34982 this._postLayout();
34984 _layoutItems : function ( items , isInstant)
34986 //this.fireEvent( 'layout', this, items );
34989 if ( !items || !items.elements.length ) {
34990 // no items, emit event with empty array
34995 items.each(function(item) {
34996 Roo.log("layout item");
34998 // get x/y object from method
34999 var position = this._getItemLayoutPosition( item );
35001 position.item = item;
35002 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35003 queue.push( position );
35006 this._processLayoutQueue( queue );
35008 /** Sets position of item in DOM
35009 * @param {Element} item
35010 * @param {Number} x - horizontal position
35011 * @param {Number} y - vertical position
35012 * @param {Boolean} isInstant - disables transitions
35014 _processLayoutQueue : function( queue )
35016 for ( var i=0, len = queue.length; i < len; i++ ) {
35017 var obj = queue[i];
35018 obj.item.position('absolute');
35019 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35025 * Any logic you want to do after each layout,
35026 * i.e. size the container
35028 _postLayout : function()
35030 this.resizeContainer();
35033 resizeContainer : function()
35035 if ( !this.isResizingContainer ) {
35038 var size = this._getContainerSize();
35040 this.el.setSize(size.width,size.height);
35041 this.boxesEl.setSize(size.width,size.height);
35047 _resetLayout : function()
35049 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35050 this.colWidth = this.el.getWidth();
35051 //this.gutter = this.el.getWidth();
35053 this.measureColumns();
35059 this.colYs.push( 0 );
35065 measureColumns : function()
35067 this.getContainerWidth();
35068 // if columnWidth is 0, default to outerWidth of first item
35069 if ( !this.columnWidth ) {
35070 var firstItem = this.bricks.first();
35071 Roo.log(firstItem);
35072 this.columnWidth = this.containerWidth;
35073 if (firstItem && firstItem.attr('originalwidth') ) {
35074 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35076 // columnWidth fall back to item of first element
35077 Roo.log("set column width?");
35078 this.initialColumnWidth = this.columnWidth ;
35080 // if first elem has no width, default to size of container
35085 if (this.initialColumnWidth) {
35086 this.columnWidth = this.initialColumnWidth;
35091 // column width is fixed at the top - however if container width get's smaller we should
35094 // this bit calcs how man columns..
35096 var columnWidth = this.columnWidth += this.gutter;
35098 // calculate columns
35099 var containerWidth = this.containerWidth + this.gutter;
35101 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35102 // fix rounding errors, typically with gutters
35103 var excess = columnWidth - containerWidth % columnWidth;
35106 // if overshoot is less than a pixel, round up, otherwise floor it
35107 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35108 cols = Math[ mathMethod ]( cols );
35109 this.cols = Math.max( cols, 1 );
35110 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35112 // padding positioning..
35113 var totalColWidth = this.cols * this.columnWidth;
35114 var padavail = this.containerWidth - totalColWidth;
35115 // so for 2 columns - we need 3 'pads'
35117 var padNeeded = (1+this.cols) * this.padWidth;
35119 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35121 this.columnWidth += padExtra
35122 //this.padWidth = Math.floor(padavail / ( this.cols));
35124 // adjust colum width so that padding is fixed??
35126 // we have 3 columns ... total = width * 3
35127 // we have X left over... that should be used by
35129 //if (this.expandC) {
35137 getContainerWidth : function()
35139 /* // container is parent if fit width
35140 var container = this.isFitWidth ? this.element.parentNode : this.element;
35141 // check that this.size and size are there
35142 // IE8 triggers resize on body size change, so they might not be
35144 var size = getSize( container ); //FIXME
35145 this.containerWidth = size && size.innerWidth; //FIXME
35148 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35152 _getItemLayoutPosition : function( item ) // what is item?
35154 // we resize the item to our columnWidth..
35156 item.setWidth(this.columnWidth);
35157 item.autoBoxAdjust = false;
35159 var sz = item.getSize();
35161 // how many columns does this brick span
35162 var remainder = this.containerWidth % this.columnWidth;
35164 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35165 // round if off by 1 pixel, otherwise use ceil
35166 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35167 colSpan = Math.min( colSpan, this.cols );
35169 // normally this should be '1' as we dont' currently allow multi width columns..
35171 var colGroup = this._getColGroup( colSpan );
35172 // get the minimum Y value from the columns
35173 var minimumY = Math.min.apply( Math, colGroup );
35174 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35176 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35178 // position the brick
35180 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35181 y: this.currentSize.y + minimumY + this.padHeight
35185 // apply setHeight to necessary columns
35186 var setHeight = minimumY + sz.height + this.padHeight;
35187 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35189 var setSpan = this.cols + 1 - colGroup.length;
35190 for ( var i = 0; i < setSpan; i++ ) {
35191 this.colYs[ shortColIndex + i ] = setHeight ;
35198 * @param {Number} colSpan - number of columns the element spans
35199 * @returns {Array} colGroup
35201 _getColGroup : function( colSpan )
35203 if ( colSpan < 2 ) {
35204 // if brick spans only one column, use all the column Ys
35209 // how many different places could this brick fit horizontally
35210 var groupCount = this.cols + 1 - colSpan;
35211 // for each group potential horizontal position
35212 for ( var i = 0; i < groupCount; i++ ) {
35213 // make an array of colY values for that one group
35214 var groupColYs = this.colYs.slice( i, i + colSpan );
35215 // and get the max value of the array
35216 colGroup[i] = Math.max.apply( Math, groupColYs );
35221 _manageStamp : function( stamp )
35223 var stampSize = stamp.getSize();
35224 var offset = stamp.getBox();
35225 // get the columns that this stamp affects
35226 var firstX = this.isOriginLeft ? offset.x : offset.right;
35227 var lastX = firstX + stampSize.width;
35228 var firstCol = Math.floor( firstX / this.columnWidth );
35229 firstCol = Math.max( 0, firstCol );
35231 var lastCol = Math.floor( lastX / this.columnWidth );
35232 // lastCol should not go over if multiple of columnWidth #425
35233 lastCol -= lastX % this.columnWidth ? 0 : 1;
35234 lastCol = Math.min( this.cols - 1, lastCol );
35236 // set colYs to bottom of the stamp
35237 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35240 for ( var i = firstCol; i <= lastCol; i++ ) {
35241 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35246 _getContainerSize : function()
35248 this.maxY = Math.max.apply( Math, this.colYs );
35253 if ( this.isFitWidth ) {
35254 size.width = this._getContainerFitWidth();
35260 _getContainerFitWidth : function()
35262 var unusedCols = 0;
35263 // count unused columns
35266 if ( this.colYs[i] !== 0 ) {
35271 // fit container to columns that have been used
35272 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35275 needsResizeLayout : function()
35277 var previousWidth = this.containerWidth;
35278 this.getContainerWidth();
35279 return previousWidth !== this.containerWidth;
35294 * @class Roo.bootstrap.MasonryBrick
35295 * @extends Roo.bootstrap.Component
35296 * Bootstrap MasonryBrick class
35299 * Create a new MasonryBrick
35300 * @param {Object} config The config object
35303 Roo.bootstrap.MasonryBrick = function(config){
35305 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35307 Roo.bootstrap.MasonryBrick.register(this);
35313 * When a MasonryBrick is clcik
35314 * @param {Roo.bootstrap.MasonryBrick} this
35315 * @param {Roo.EventObject} e
35321 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35324 * @cfg {String} title
35328 * @cfg {String} html
35332 * @cfg {String} bgimage
35336 * @cfg {String} videourl
35340 * @cfg {String} cls
35344 * @cfg {String} href
35348 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35353 * @cfg {String} placetitle (center|bottom)
35358 * @cfg {Boolean} isFitContainer defalut true
35360 isFitContainer : true,
35363 * @cfg {Boolean} preventDefault defalut false
35365 preventDefault : false,
35368 * @cfg {Boolean} inverse defalut false
35370 maskInverse : false,
35372 getAutoCreate : function()
35374 if(!this.isFitContainer){
35375 return this.getSplitAutoCreate();
35378 var cls = 'masonry-brick masonry-brick-full';
35380 if(this.href.length){
35381 cls += ' masonry-brick-link';
35384 if(this.bgimage.length){
35385 cls += ' masonry-brick-image';
35388 if(this.maskInverse){
35389 cls += ' mask-inverse';
35392 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35393 cls += ' enable-mask';
35397 cls += ' masonry-' + this.size + '-brick';
35400 if(this.placetitle.length){
35402 switch (this.placetitle) {
35404 cls += ' masonry-center-title';
35407 cls += ' masonry-bottom-title';
35414 if(!this.html.length && !this.bgimage.length){
35415 cls += ' masonry-center-title';
35418 if(!this.html.length && this.bgimage.length){
35419 cls += ' masonry-bottom-title';
35424 cls += ' ' + this.cls;
35428 tag: (this.href.length) ? 'a' : 'div',
35433 cls: 'masonry-brick-mask'
35437 cls: 'masonry-brick-paragraph',
35443 if(this.href.length){
35444 cfg.href = this.href;
35447 var cn = cfg.cn[1].cn;
35449 if(this.title.length){
35452 cls: 'masonry-brick-title',
35457 if(this.html.length){
35460 cls: 'masonry-brick-text',
35465 if (!this.title.length && !this.html.length) {
35466 cfg.cn[1].cls += ' hide';
35469 if(this.bgimage.length){
35472 cls: 'masonry-brick-image-view',
35477 if(this.videourl.length){
35478 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35479 // youtube support only?
35482 cls: 'masonry-brick-image-view',
35485 allowfullscreen : true
35493 getSplitAutoCreate : function()
35495 var cls = 'masonry-brick masonry-brick-split';
35497 if(this.href.length){
35498 cls += ' masonry-brick-link';
35501 if(this.bgimage.length){
35502 cls += ' masonry-brick-image';
35506 cls += ' masonry-' + this.size + '-brick';
35509 switch (this.placetitle) {
35511 cls += ' masonry-center-title';
35514 cls += ' masonry-bottom-title';
35517 if(!this.bgimage.length){
35518 cls += ' masonry-center-title';
35521 if(this.bgimage.length){
35522 cls += ' masonry-bottom-title';
35528 cls += ' ' + this.cls;
35532 tag: (this.href.length) ? 'a' : 'div',
35537 cls: 'masonry-brick-split-head',
35541 cls: 'masonry-brick-paragraph',
35548 cls: 'masonry-brick-split-body',
35554 if(this.href.length){
35555 cfg.href = this.href;
35558 if(this.title.length){
35559 cfg.cn[0].cn[0].cn.push({
35561 cls: 'masonry-brick-title',
35566 if(this.html.length){
35567 cfg.cn[1].cn.push({
35569 cls: 'masonry-brick-text',
35574 if(this.bgimage.length){
35575 cfg.cn[0].cn.push({
35577 cls: 'masonry-brick-image-view',
35582 if(this.videourl.length){
35583 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35584 // youtube support only?
35585 cfg.cn[0].cn.cn.push({
35587 cls: 'masonry-brick-image-view',
35590 allowfullscreen : true
35597 initEvents: function()
35599 switch (this.size) {
35632 this.el.on('touchstart', this.onTouchStart, this);
35633 this.el.on('touchmove', this.onTouchMove, this);
35634 this.el.on('touchend', this.onTouchEnd, this);
35635 this.el.on('contextmenu', this.onContextMenu, this);
35637 this.el.on('mouseenter' ,this.enter, this);
35638 this.el.on('mouseleave', this.leave, this);
35639 this.el.on('click', this.onClick, this);
35642 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35643 this.parent().bricks.push(this);
35648 onClick: function(e, el)
35650 var time = this.endTimer - this.startTimer;
35651 // Roo.log(e.preventDefault());
35654 e.preventDefault();
35659 if(!this.preventDefault){
35663 e.preventDefault();
35665 if (this.activeClass != '') {
35666 this.selectBrick();
35669 this.fireEvent('click', this, e);
35672 enter: function(e, el)
35674 e.preventDefault();
35676 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35680 if(this.bgimage.length && this.html.length){
35681 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35685 leave: function(e, el)
35687 e.preventDefault();
35689 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35693 if(this.bgimage.length && this.html.length){
35694 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35698 onTouchStart: function(e, el)
35700 // e.preventDefault();
35702 this.touchmoved = false;
35704 if(!this.isFitContainer){
35708 if(!this.bgimage.length || !this.html.length){
35712 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35714 this.timer = new Date().getTime();
35718 onTouchMove: function(e, el)
35720 this.touchmoved = true;
35723 onContextMenu : function(e,el)
35725 e.preventDefault();
35726 e.stopPropagation();
35730 onTouchEnd: function(e, el)
35732 // e.preventDefault();
35734 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35741 if(!this.bgimage.length || !this.html.length){
35743 if(this.href.length){
35744 window.location.href = this.href;
35750 if(!this.isFitContainer){
35754 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35756 window.location.href = this.href;
35759 //selection on single brick only
35760 selectBrick : function() {
35762 if (!this.parentId) {
35766 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35767 var index = m.selectedBrick.indexOf(this.id);
35770 m.selectedBrick.splice(index,1);
35771 this.el.removeClass(this.activeClass);
35775 for(var i = 0; i < m.selectedBrick.length; i++) {
35776 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35777 b.el.removeClass(b.activeClass);
35780 m.selectedBrick = [];
35782 m.selectedBrick.push(this.id);
35783 this.el.addClass(this.activeClass);
35787 isSelected : function(){
35788 return this.el.hasClass(this.activeClass);
35793 Roo.apply(Roo.bootstrap.MasonryBrick, {
35796 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35798 * register a Masonry Brick
35799 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35802 register : function(brick)
35804 //this.groups[brick.id] = brick;
35805 this.groups.add(brick.id, brick);
35808 * fetch a masonry brick based on the masonry brick ID
35809 * @param {string} the masonry brick to add
35810 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35813 get: function(brick_id)
35815 // if (typeof(this.groups[brick_id]) == 'undefined') {
35818 // return this.groups[brick_id] ;
35820 if(this.groups.key(brick_id)) {
35821 return this.groups.key(brick_id);
35839 * @class Roo.bootstrap.Brick
35840 * @extends Roo.bootstrap.Component
35841 * Bootstrap Brick class
35844 * Create a new Brick
35845 * @param {Object} config The config object
35848 Roo.bootstrap.Brick = function(config){
35849 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35855 * When a Brick is click
35856 * @param {Roo.bootstrap.Brick} this
35857 * @param {Roo.EventObject} e
35863 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35866 * @cfg {String} title
35870 * @cfg {String} html
35874 * @cfg {String} bgimage
35878 * @cfg {String} cls
35882 * @cfg {String} href
35886 * @cfg {String} video
35890 * @cfg {Boolean} square
35894 getAutoCreate : function()
35896 var cls = 'roo-brick';
35898 if(this.href.length){
35899 cls += ' roo-brick-link';
35902 if(this.bgimage.length){
35903 cls += ' roo-brick-image';
35906 if(!this.html.length && !this.bgimage.length){
35907 cls += ' roo-brick-center-title';
35910 if(!this.html.length && this.bgimage.length){
35911 cls += ' roo-brick-bottom-title';
35915 cls += ' ' + this.cls;
35919 tag: (this.href.length) ? 'a' : 'div',
35924 cls: 'roo-brick-paragraph',
35930 if(this.href.length){
35931 cfg.href = this.href;
35934 var cn = cfg.cn[0].cn;
35936 if(this.title.length){
35939 cls: 'roo-brick-title',
35944 if(this.html.length){
35947 cls: 'roo-brick-text',
35954 if(this.bgimage.length){
35957 cls: 'roo-brick-image-view',
35965 initEvents: function()
35967 if(this.title.length || this.html.length){
35968 this.el.on('mouseenter' ,this.enter, this);
35969 this.el.on('mouseleave', this.leave, this);
35972 Roo.EventManager.onWindowResize(this.resize, this);
35974 if(this.bgimage.length){
35975 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35976 this.imageEl.on('load', this.onImageLoad, this);
35983 onImageLoad : function()
35988 resize : function()
35990 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35992 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35994 if(this.bgimage.length){
35995 var image = this.el.select('.roo-brick-image-view', true).first();
35997 image.setWidth(paragraph.getWidth());
36000 image.setHeight(paragraph.getWidth());
36003 this.el.setHeight(image.getHeight());
36004 paragraph.setHeight(image.getHeight());
36010 enter: function(e, el)
36012 e.preventDefault();
36014 if(this.bgimage.length){
36015 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36016 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36020 leave: function(e, el)
36022 e.preventDefault();
36024 if(this.bgimage.length){
36025 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36026 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36041 * @class Roo.bootstrap.NumberField
36042 * @extends Roo.bootstrap.Input
36043 * Bootstrap NumberField class
36049 * Create a new NumberField
36050 * @param {Object} config The config object
36053 Roo.bootstrap.NumberField = function(config){
36054 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36057 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36060 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36062 allowDecimals : true,
36064 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36066 decimalSeparator : ".",
36068 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36070 decimalPrecision : 2,
36072 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36074 allowNegative : true,
36077 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36081 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36083 minValue : Number.NEGATIVE_INFINITY,
36085 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36087 maxValue : Number.MAX_VALUE,
36089 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36091 minText : "The minimum value for this field is {0}",
36093 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36095 maxText : "The maximum value for this field is {0}",
36097 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36098 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36100 nanText : "{0} is not a valid number",
36102 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36104 thousandsDelimiter : false,
36106 * @cfg {String} valueAlign alignment of value
36108 valueAlign : "left",
36110 getAutoCreate : function()
36112 var hiddenInput = {
36116 cls: 'hidden-number-input'
36120 hiddenInput.name = this.name;
36125 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36127 this.name = hiddenInput.name;
36129 if(cfg.cn.length > 0) {
36130 cfg.cn.push(hiddenInput);
36137 initEvents : function()
36139 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36141 var allowed = "0123456789";
36143 if(this.allowDecimals){
36144 allowed += this.decimalSeparator;
36147 if(this.allowNegative){
36151 if(this.thousandsDelimiter) {
36155 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36157 var keyPress = function(e){
36159 var k = e.getKey();
36161 var c = e.getCharCode();
36164 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36165 allowed.indexOf(String.fromCharCode(c)) === -1
36171 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36175 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36180 this.el.on("keypress", keyPress, this);
36183 validateValue : function(value)
36186 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36190 var num = this.parseValue(value);
36193 this.markInvalid(String.format(this.nanText, value));
36197 if(num < this.minValue){
36198 this.markInvalid(String.format(this.minText, this.minValue));
36202 if(num > this.maxValue){
36203 this.markInvalid(String.format(this.maxText, this.maxValue));
36210 getValue : function()
36212 var v = this.hiddenEl().getValue();
36214 return this.fixPrecision(this.parseValue(v));
36217 parseValue : function(value)
36219 if(this.thousandsDelimiter) {
36221 r = new RegExp(",", "g");
36222 value = value.replace(r, "");
36225 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36226 return isNaN(value) ? '' : value;
36229 fixPrecision : function(value)
36231 if(this.thousandsDelimiter) {
36233 r = new RegExp(",", "g");
36234 value = value.replace(r, "");
36237 var nan = isNaN(value);
36239 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36240 return nan ? '' : value;
36242 return parseFloat(value).toFixed(this.decimalPrecision);
36245 setValue : function(v)
36247 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36253 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36255 this.inputEl().dom.value = (v == '') ? '' :
36256 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36258 if(!this.allowZero && v === '0') {
36259 this.hiddenEl().dom.value = '';
36260 this.inputEl().dom.value = '';
36267 decimalPrecisionFcn : function(v)
36269 return Math.floor(v);
36272 beforeBlur : function()
36274 var v = this.parseValue(this.getRawValue());
36276 if(v || v === 0 || v === ''){
36281 hiddenEl : function()
36283 return this.el.select('input.hidden-number-input',true).first();
36295 * @class Roo.bootstrap.DocumentSlider
36296 * @extends Roo.bootstrap.Component
36297 * Bootstrap DocumentSlider class
36300 * Create a new DocumentViewer
36301 * @param {Object} config The config object
36304 Roo.bootstrap.DocumentSlider = function(config){
36305 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36312 * Fire after initEvent
36313 * @param {Roo.bootstrap.DocumentSlider} this
36318 * Fire after update
36319 * @param {Roo.bootstrap.DocumentSlider} this
36325 * @param {Roo.bootstrap.DocumentSlider} this
36331 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36337 getAutoCreate : function()
36341 cls : 'roo-document-slider',
36345 cls : 'roo-document-slider-header',
36349 cls : 'roo-document-slider-header-title'
36355 cls : 'roo-document-slider-body',
36359 cls : 'roo-document-slider-prev',
36363 cls : 'fa fa-chevron-left'
36369 cls : 'roo-document-slider-thumb',
36373 cls : 'roo-document-slider-image'
36379 cls : 'roo-document-slider-next',
36383 cls : 'fa fa-chevron-right'
36395 initEvents : function()
36397 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36398 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36400 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36401 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36403 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36404 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36406 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36407 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36409 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36410 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36412 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36413 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36415 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36416 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36418 this.thumbEl.on('click', this.onClick, this);
36420 this.prevIndicator.on('click', this.prev, this);
36422 this.nextIndicator.on('click', this.next, this);
36426 initial : function()
36428 if(this.files.length){
36429 this.indicator = 1;
36433 this.fireEvent('initial', this);
36436 update : function()
36438 this.imageEl.attr('src', this.files[this.indicator - 1]);
36440 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36442 this.prevIndicator.show();
36444 if(this.indicator == 1){
36445 this.prevIndicator.hide();
36448 this.nextIndicator.show();
36450 if(this.indicator == this.files.length){
36451 this.nextIndicator.hide();
36454 this.thumbEl.scrollTo('top');
36456 this.fireEvent('update', this);
36459 onClick : function(e)
36461 e.preventDefault();
36463 this.fireEvent('click', this);
36468 e.preventDefault();
36470 this.indicator = Math.max(1, this.indicator - 1);
36477 e.preventDefault();
36479 this.indicator = Math.min(this.files.length, this.indicator + 1);
36493 * @class Roo.bootstrap.RadioSet
36494 * @extends Roo.bootstrap.Input
36495 * Bootstrap RadioSet class
36496 * @cfg {String} indicatorpos (left|right) default left
36497 * @cfg {Boolean} inline (true|false) inline the element (default true)
36498 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36500 * Create a new RadioSet
36501 * @param {Object} config The config object
36504 Roo.bootstrap.RadioSet = function(config){
36506 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36510 Roo.bootstrap.RadioSet.register(this);
36515 * Fires when the element is checked or unchecked.
36516 * @param {Roo.bootstrap.RadioSet} this This radio
36517 * @param {Roo.bootstrap.Radio} item The checked item
36522 * Fires when the element is click.
36523 * @param {Roo.bootstrap.RadioSet} this This radio set
36524 * @param {Roo.bootstrap.Radio} item The checked item
36525 * @param {Roo.EventObject} e The event object
36532 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36540 indicatorpos : 'left',
36542 getAutoCreate : function()
36546 cls : 'roo-radio-set-label',
36550 html : this.fieldLabel
36554 if (Roo.bootstrap.version == 3) {
36557 if(this.indicatorpos == 'left'){
36560 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36561 tooltip : 'This field is required'
36566 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36567 tooltip : 'This field is required'
36573 cls : 'roo-radio-set-items'
36576 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36578 if (align === 'left' && this.fieldLabel.length) {
36581 cls : "roo-radio-set-right",
36587 if(this.labelWidth > 12){
36588 label.style = "width: " + this.labelWidth + 'px';
36591 if(this.labelWidth < 13 && this.labelmd == 0){
36592 this.labelmd = this.labelWidth;
36595 if(this.labellg > 0){
36596 label.cls += ' col-lg-' + this.labellg;
36597 items.cls += ' col-lg-' + (12 - this.labellg);
36600 if(this.labelmd > 0){
36601 label.cls += ' col-md-' + this.labelmd;
36602 items.cls += ' col-md-' + (12 - this.labelmd);
36605 if(this.labelsm > 0){
36606 label.cls += ' col-sm-' + this.labelsm;
36607 items.cls += ' col-sm-' + (12 - this.labelsm);
36610 if(this.labelxs > 0){
36611 label.cls += ' col-xs-' + this.labelxs;
36612 items.cls += ' col-xs-' + (12 - this.labelxs);
36618 cls : 'roo-radio-set',
36622 cls : 'roo-radio-set-input',
36625 value : this.value ? this.value : ''
36632 if(this.weight.length){
36633 cfg.cls += ' roo-radio-' + this.weight;
36637 cfg.cls += ' roo-radio-set-inline';
36641 ['xs','sm','md','lg'].map(function(size){
36642 if (settings[size]) {
36643 cfg.cls += ' col-' + size + '-' + settings[size];
36651 initEvents : function()
36653 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36654 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36656 if(!this.fieldLabel.length){
36657 this.labelEl.hide();
36660 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36661 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36663 this.indicator = this.indicatorEl();
36665 if(this.indicator){
36666 this.indicator.addClass('invisible');
36669 this.originalValue = this.getValue();
36673 inputEl: function ()
36675 return this.el.select('.roo-radio-set-input', true).first();
36678 getChildContainer : function()
36680 return this.itemsEl;
36683 register : function(item)
36685 this.radioes.push(item);
36689 validate : function()
36691 if(this.getVisibilityEl().hasClass('hidden')){
36697 Roo.each(this.radioes, function(i){
36706 if(this.allowBlank) {
36710 if(this.disabled || valid){
36715 this.markInvalid();
36720 markValid : function()
36722 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36723 this.indicatorEl().removeClass('visible');
36724 this.indicatorEl().addClass('invisible');
36728 if (Roo.bootstrap.version == 3) {
36729 this.el.removeClass([this.invalidClass, this.validClass]);
36730 this.el.addClass(this.validClass);
36732 this.el.removeClass(['is-invalid','is-valid']);
36733 this.el.addClass(['is-valid']);
36735 this.fireEvent('valid', this);
36738 markInvalid : function(msg)
36740 if(this.allowBlank || this.disabled){
36744 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36745 this.indicatorEl().removeClass('invisible');
36746 this.indicatorEl().addClass('visible');
36748 if (Roo.bootstrap.version == 3) {
36749 this.el.removeClass([this.invalidClass, this.validClass]);
36750 this.el.addClass(this.invalidClass);
36752 this.el.removeClass(['is-invalid','is-valid']);
36753 this.el.addClass(['is-invalid']);
36756 this.fireEvent('invalid', this, msg);
36760 setValue : function(v, suppressEvent)
36762 if(this.value === v){
36769 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36772 Roo.each(this.radioes, function(i){
36774 i.el.removeClass('checked');
36777 Roo.each(this.radioes, function(i){
36779 if(i.value === v || i.value.toString() === v.toString()){
36781 i.el.addClass('checked');
36783 if(suppressEvent !== true){
36784 this.fireEvent('check', this, i);
36795 clearInvalid : function(){
36797 if(!this.el || this.preventMark){
36801 this.el.removeClass([this.invalidClass]);
36803 this.fireEvent('valid', this);
36808 Roo.apply(Roo.bootstrap.RadioSet, {
36812 register : function(set)
36814 this.groups[set.name] = set;
36817 get: function(name)
36819 if (typeof(this.groups[name]) == 'undefined') {
36823 return this.groups[name] ;
36829 * Ext JS Library 1.1.1
36830 * Copyright(c) 2006-2007, Ext JS, LLC.
36832 * Originally Released Under LGPL - original licence link has changed is not relivant.
36835 * <script type="text/javascript">
36840 * @class Roo.bootstrap.SplitBar
36841 * @extends Roo.util.Observable
36842 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36846 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36847 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36848 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36849 split.minSize = 100;
36850 split.maxSize = 600;
36851 split.animate = true;
36852 split.on('moved', splitterMoved);
36855 * Create a new SplitBar
36856 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36857 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36858 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36859 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36860 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36861 position of the SplitBar).
36863 Roo.bootstrap.SplitBar = function(cfg){
36868 // dragElement : elm
36869 // resizingElement: el,
36871 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36872 // placement : Roo.bootstrap.SplitBar.LEFT ,
36873 // existingProxy ???
36876 this.el = Roo.get(cfg.dragElement, true);
36877 this.el.dom.unselectable = "on";
36879 this.resizingEl = Roo.get(cfg.resizingElement, true);
36883 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36884 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36887 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36890 * The minimum size of the resizing element. (Defaults to 0)
36896 * The maximum size of the resizing element. (Defaults to 2000)
36899 this.maxSize = 2000;
36902 * Whether to animate the transition to the new size
36905 this.animate = false;
36908 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36911 this.useShim = false;
36916 if(!cfg.existingProxy){
36918 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36920 this.proxy = Roo.get(cfg.existingProxy).dom;
36923 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36926 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36929 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36932 this.dragSpecs = {};
36935 * @private The adapter to use to positon and resize elements
36937 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36938 this.adapter.init(this);
36940 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36942 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36943 this.el.addClass("roo-splitbar-h");
36946 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36947 this.el.addClass("roo-splitbar-v");
36953 * Fires when the splitter is moved (alias for {@link #event-moved})
36954 * @param {Roo.bootstrap.SplitBar} this
36955 * @param {Number} newSize the new width or height
36960 * Fires when the splitter is moved
36961 * @param {Roo.bootstrap.SplitBar} this
36962 * @param {Number} newSize the new width or height
36966 * @event beforeresize
36967 * Fires before the splitter is dragged
36968 * @param {Roo.bootstrap.SplitBar} this
36970 "beforeresize" : true,
36972 "beforeapply" : true
36975 Roo.util.Observable.call(this);
36978 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36979 onStartProxyDrag : function(x, y){
36980 this.fireEvent("beforeresize", this);
36982 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36984 o.enableDisplayMode("block");
36985 // all splitbars share the same overlay
36986 Roo.bootstrap.SplitBar.prototype.overlay = o;
36988 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36989 this.overlay.show();
36990 Roo.get(this.proxy).setDisplayed("block");
36991 var size = this.adapter.getElementSize(this);
36992 this.activeMinSize = this.getMinimumSize();;
36993 this.activeMaxSize = this.getMaximumSize();;
36994 var c1 = size - this.activeMinSize;
36995 var c2 = Math.max(this.activeMaxSize - size, 0);
36996 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36997 this.dd.resetConstraints();
36998 this.dd.setXConstraint(
36999 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37000 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37002 this.dd.setYConstraint(0, 0);
37004 this.dd.resetConstraints();
37005 this.dd.setXConstraint(0, 0);
37006 this.dd.setYConstraint(
37007 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37008 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37011 this.dragSpecs.startSize = size;
37012 this.dragSpecs.startPoint = [x, y];
37013 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37017 * @private Called after the drag operation by the DDProxy
37019 onEndProxyDrag : function(e){
37020 Roo.get(this.proxy).setDisplayed(false);
37021 var endPoint = Roo.lib.Event.getXY(e);
37023 this.overlay.hide();
37026 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37027 newSize = this.dragSpecs.startSize +
37028 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37029 endPoint[0] - this.dragSpecs.startPoint[0] :
37030 this.dragSpecs.startPoint[0] - endPoint[0]
37033 newSize = this.dragSpecs.startSize +
37034 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37035 endPoint[1] - this.dragSpecs.startPoint[1] :
37036 this.dragSpecs.startPoint[1] - endPoint[1]
37039 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37040 if(newSize != this.dragSpecs.startSize){
37041 if(this.fireEvent('beforeapply', this, newSize) !== false){
37042 this.adapter.setElementSize(this, newSize);
37043 this.fireEvent("moved", this, newSize);
37044 this.fireEvent("resize", this, newSize);
37050 * Get the adapter this SplitBar uses
37051 * @return The adapter object
37053 getAdapter : function(){
37054 return this.adapter;
37058 * Set the adapter this SplitBar uses
37059 * @param {Object} adapter A SplitBar adapter object
37061 setAdapter : function(adapter){
37062 this.adapter = adapter;
37063 this.adapter.init(this);
37067 * Gets the minimum size for the resizing element
37068 * @return {Number} The minimum size
37070 getMinimumSize : function(){
37071 return this.minSize;
37075 * Sets the minimum size for the resizing element
37076 * @param {Number} minSize The minimum size
37078 setMinimumSize : function(minSize){
37079 this.minSize = minSize;
37083 * Gets the maximum size for the resizing element
37084 * @return {Number} The maximum size
37086 getMaximumSize : function(){
37087 return this.maxSize;
37091 * Sets the maximum size for the resizing element
37092 * @param {Number} maxSize The maximum size
37094 setMaximumSize : function(maxSize){
37095 this.maxSize = maxSize;
37099 * Sets the initialize size for the resizing element
37100 * @param {Number} size The initial size
37102 setCurrentSize : function(size){
37103 var oldAnimate = this.animate;
37104 this.animate = false;
37105 this.adapter.setElementSize(this, size);
37106 this.animate = oldAnimate;
37110 * Destroy this splitbar.
37111 * @param {Boolean} removeEl True to remove the element
37113 destroy : function(removeEl){
37115 this.shim.remove();
37118 this.proxy.parentNode.removeChild(this.proxy);
37126 * @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.
37128 Roo.bootstrap.SplitBar.createProxy = function(dir){
37129 var proxy = new Roo.Element(document.createElement("div"));
37130 proxy.unselectable();
37131 var cls = 'roo-splitbar-proxy';
37132 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37133 document.body.appendChild(proxy.dom);
37138 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37139 * Default Adapter. It assumes the splitter and resizing element are not positioned
37140 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37142 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37145 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37146 // do nothing for now
37147 init : function(s){
37151 * Called before drag operations to get the current size of the resizing element.
37152 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37154 getElementSize : function(s){
37155 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37156 return s.resizingEl.getWidth();
37158 return s.resizingEl.getHeight();
37163 * Called after drag operations to set the size of the resizing element.
37164 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37165 * @param {Number} newSize The new size to set
37166 * @param {Function} onComplete A function to be invoked when resizing is complete
37168 setElementSize : function(s, newSize, onComplete){
37169 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37171 s.resizingEl.setWidth(newSize);
37173 onComplete(s, newSize);
37176 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37181 s.resizingEl.setHeight(newSize);
37183 onComplete(s, newSize);
37186 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37193 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37194 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37195 * Adapter that moves the splitter element to align with the resized sizing element.
37196 * Used with an absolute positioned SplitBar.
37197 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37198 * document.body, make sure you assign an id to the body element.
37200 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37201 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37202 this.container = Roo.get(container);
37205 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37206 init : function(s){
37207 this.basic.init(s);
37210 getElementSize : function(s){
37211 return this.basic.getElementSize(s);
37214 setElementSize : function(s, newSize, onComplete){
37215 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37218 moveSplitter : function(s){
37219 var yes = Roo.bootstrap.SplitBar;
37220 switch(s.placement){
37222 s.el.setX(s.resizingEl.getRight());
37225 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37228 s.el.setY(s.resizingEl.getBottom());
37231 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37238 * Orientation constant - Create a vertical SplitBar
37242 Roo.bootstrap.SplitBar.VERTICAL = 1;
37245 * Orientation constant - Create a horizontal SplitBar
37249 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37252 * Placement constant - The resizing element is to the left of the splitter element
37256 Roo.bootstrap.SplitBar.LEFT = 1;
37259 * Placement constant - The resizing element is to the right of the splitter element
37263 Roo.bootstrap.SplitBar.RIGHT = 2;
37266 * Placement constant - The resizing element is positioned above the splitter element
37270 Roo.bootstrap.SplitBar.TOP = 3;
37273 * Placement constant - The resizing element is positioned under splitter element
37277 Roo.bootstrap.SplitBar.BOTTOM = 4;
37278 Roo.namespace("Roo.bootstrap.layout");/*
37280 * Ext JS Library 1.1.1
37281 * Copyright(c) 2006-2007, Ext JS, LLC.
37283 * Originally Released Under LGPL - original licence link has changed is not relivant.
37286 * <script type="text/javascript">
37290 * @class Roo.bootstrap.layout.Manager
37291 * @extends Roo.bootstrap.Component
37292 * Base class for layout managers.
37294 Roo.bootstrap.layout.Manager = function(config)
37296 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37302 /** false to disable window resize monitoring @type Boolean */
37303 this.monitorWindowResize = true;
37308 * Fires when a layout is performed.
37309 * @param {Roo.LayoutManager} this
37313 * @event regionresized
37314 * Fires when the user resizes a region.
37315 * @param {Roo.LayoutRegion} region The resized region
37316 * @param {Number} newSize The new size (width for east/west, height for north/south)
37318 "regionresized" : true,
37320 * @event regioncollapsed
37321 * Fires when a region is collapsed.
37322 * @param {Roo.LayoutRegion} region The collapsed region
37324 "regioncollapsed" : true,
37326 * @event regionexpanded
37327 * Fires when a region is expanded.
37328 * @param {Roo.LayoutRegion} region The expanded region
37330 "regionexpanded" : true
37332 this.updating = false;
37335 this.el = Roo.get(config.el);
37341 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37346 monitorWindowResize : true,
37352 onRender : function(ct, position)
37355 this.el = Roo.get(ct);
37358 //this.fireEvent('render',this);
37362 initEvents: function()
37366 // ie scrollbar fix
37367 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37368 document.body.scroll = "no";
37369 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37370 this.el.position('relative');
37372 this.id = this.el.id;
37373 this.el.addClass("roo-layout-container");
37374 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37375 if(this.el.dom != document.body ) {
37376 this.el.on('resize', this.layout,this);
37377 this.el.on('show', this.layout,this);
37383 * Returns true if this layout is currently being updated
37384 * @return {Boolean}
37386 isUpdating : function(){
37387 return this.updating;
37391 * Suspend the LayoutManager from doing auto-layouts while
37392 * making multiple add or remove calls
37394 beginUpdate : function(){
37395 this.updating = true;
37399 * Restore auto-layouts and optionally disable the manager from performing a layout
37400 * @param {Boolean} noLayout true to disable a layout update
37402 endUpdate : function(noLayout){
37403 this.updating = false;
37409 layout: function(){
37413 onRegionResized : function(region, newSize){
37414 this.fireEvent("regionresized", region, newSize);
37418 onRegionCollapsed : function(region){
37419 this.fireEvent("regioncollapsed", region);
37422 onRegionExpanded : function(region){
37423 this.fireEvent("regionexpanded", region);
37427 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37428 * performs box-model adjustments.
37429 * @return {Object} The size as an object {width: (the width), height: (the height)}
37431 getViewSize : function()
37434 if(this.el.dom != document.body){
37435 size = this.el.getSize();
37437 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37439 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37440 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37445 * Returns the Element this layout is bound to.
37446 * @return {Roo.Element}
37448 getEl : function(){
37453 * Returns the specified region.
37454 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37455 * @return {Roo.LayoutRegion}
37457 getRegion : function(target){
37458 return this.regions[target.toLowerCase()];
37461 onWindowResize : function(){
37462 if(this.monitorWindowResize){
37469 * Ext JS Library 1.1.1
37470 * Copyright(c) 2006-2007, Ext JS, LLC.
37472 * Originally Released Under LGPL - original licence link has changed is not relivant.
37475 * <script type="text/javascript">
37478 * @class Roo.bootstrap.layout.Border
37479 * @extends Roo.bootstrap.layout.Manager
37480 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37481 * please see: examples/bootstrap/nested.html<br><br>
37483 <b>The container the layout is rendered into can be either the body element or any other element.
37484 If it is not the body element, the container needs to either be an absolute positioned element,
37485 or you will need to add "position:relative" to the css of the container. You will also need to specify
37486 the container size if it is not the body element.</b>
37489 * Create a new Border
37490 * @param {Object} config Configuration options
37492 Roo.bootstrap.layout.Border = function(config){
37493 config = config || {};
37494 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37498 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37499 if(config[region]){
37500 config[region].region = region;
37501 this.addRegion(config[region]);
37507 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37509 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37511 parent : false, // this might point to a 'nest' or a ???
37514 * Creates and adds a new region if it doesn't already exist.
37515 * @param {String} target The target region key (north, south, east, west or center).
37516 * @param {Object} config The regions config object
37517 * @return {BorderLayoutRegion} The new region
37519 addRegion : function(config)
37521 if(!this.regions[config.region]){
37522 var r = this.factory(config);
37523 this.bindRegion(r);
37525 return this.regions[config.region];
37529 bindRegion : function(r){
37530 this.regions[r.config.region] = r;
37532 r.on("visibilitychange", this.layout, this);
37533 r.on("paneladded", this.layout, this);
37534 r.on("panelremoved", this.layout, this);
37535 r.on("invalidated", this.layout, this);
37536 r.on("resized", this.onRegionResized, this);
37537 r.on("collapsed", this.onRegionCollapsed, this);
37538 r.on("expanded", this.onRegionExpanded, this);
37542 * Performs a layout update.
37544 layout : function()
37546 if(this.updating) {
37550 // render all the rebions if they have not been done alreayd?
37551 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37552 if(this.regions[region] && !this.regions[region].bodyEl){
37553 this.regions[region].onRender(this.el)
37557 var size = this.getViewSize();
37558 var w = size.width;
37559 var h = size.height;
37564 //var x = 0, y = 0;
37566 var rs = this.regions;
37567 var north = rs["north"];
37568 var south = rs["south"];
37569 var west = rs["west"];
37570 var east = rs["east"];
37571 var center = rs["center"];
37572 //if(this.hideOnLayout){ // not supported anymore
37573 //c.el.setStyle("display", "none");
37575 if(north && north.isVisible()){
37576 var b = north.getBox();
37577 var m = north.getMargins();
37578 b.width = w - (m.left+m.right);
37581 centerY = b.height + b.y + m.bottom;
37582 centerH -= centerY;
37583 north.updateBox(this.safeBox(b));
37585 if(south && south.isVisible()){
37586 var b = south.getBox();
37587 var m = south.getMargins();
37588 b.width = w - (m.left+m.right);
37590 var totalHeight = (b.height + m.top + m.bottom);
37591 b.y = h - totalHeight + m.top;
37592 centerH -= totalHeight;
37593 south.updateBox(this.safeBox(b));
37595 if(west && west.isVisible()){
37596 var b = west.getBox();
37597 var m = west.getMargins();
37598 b.height = centerH - (m.top+m.bottom);
37600 b.y = centerY + m.top;
37601 var totalWidth = (b.width + m.left + m.right);
37602 centerX += totalWidth;
37603 centerW -= totalWidth;
37604 west.updateBox(this.safeBox(b));
37606 if(east && east.isVisible()){
37607 var b = east.getBox();
37608 var m = east.getMargins();
37609 b.height = centerH - (m.top+m.bottom);
37610 var totalWidth = (b.width + m.left + m.right);
37611 b.x = w - totalWidth + m.left;
37612 b.y = centerY + m.top;
37613 centerW -= totalWidth;
37614 east.updateBox(this.safeBox(b));
37617 var m = center.getMargins();
37619 x: centerX + m.left,
37620 y: centerY + m.top,
37621 width: centerW - (m.left+m.right),
37622 height: centerH - (m.top+m.bottom)
37624 //if(this.hideOnLayout){
37625 //center.el.setStyle("display", "block");
37627 center.updateBox(this.safeBox(centerBox));
37630 this.fireEvent("layout", this);
37634 safeBox : function(box){
37635 box.width = Math.max(0, box.width);
37636 box.height = Math.max(0, box.height);
37641 * Adds a ContentPanel (or subclass) to this layout.
37642 * @param {String} target The target region key (north, south, east, west or center).
37643 * @param {Roo.ContentPanel} panel The panel to add
37644 * @return {Roo.ContentPanel} The added panel
37646 add : function(target, panel){
37648 target = target.toLowerCase();
37649 return this.regions[target].add(panel);
37653 * Remove a ContentPanel (or subclass) to this layout.
37654 * @param {String} target The target region key (north, south, east, west or center).
37655 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37656 * @return {Roo.ContentPanel} The removed panel
37658 remove : function(target, panel){
37659 target = target.toLowerCase();
37660 return this.regions[target].remove(panel);
37664 * Searches all regions for a panel with the specified id
37665 * @param {String} panelId
37666 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37668 findPanel : function(panelId){
37669 var rs = this.regions;
37670 for(var target in rs){
37671 if(typeof rs[target] != "function"){
37672 var p = rs[target].getPanel(panelId);
37682 * Searches all regions for a panel with the specified id and activates (shows) it.
37683 * @param {String/ContentPanel} panelId The panels id or the panel itself
37684 * @return {Roo.ContentPanel} The shown panel or null
37686 showPanel : function(panelId) {
37687 var rs = this.regions;
37688 for(var target in rs){
37689 var r = rs[target];
37690 if(typeof r != "function"){
37691 if(r.hasPanel(panelId)){
37692 return r.showPanel(panelId);
37700 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37701 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37704 restoreState : function(provider){
37706 provider = Roo.state.Manager;
37708 var sm = new Roo.LayoutStateManager();
37709 sm.init(this, provider);
37715 * Adds a xtype elements to the layout.
37719 xtype : 'ContentPanel',
37726 xtype : 'NestedLayoutPanel',
37732 items : [ ... list of content panels or nested layout panels.. ]
37736 * @param {Object} cfg Xtype definition of item to add.
37738 addxtype : function(cfg)
37740 // basically accepts a pannel...
37741 // can accept a layout region..!?!?
37742 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37745 // theory? children can only be panels??
37747 //if (!cfg.xtype.match(/Panel$/)) {
37752 if (typeof(cfg.region) == 'undefined') {
37753 Roo.log("Failed to add Panel, region was not set");
37757 var region = cfg.region;
37763 xitems = cfg.items;
37768 if ( region == 'center') {
37769 Roo.log("Center: " + cfg.title);
37775 case 'Content': // ContentPanel (el, cfg)
37776 case 'Scroll': // ContentPanel (el, cfg)
37778 cfg.autoCreate = cfg.autoCreate || true;
37779 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37781 // var el = this.el.createChild();
37782 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37785 this.add(region, ret);
37789 case 'TreePanel': // our new panel!
37790 cfg.el = this.el.createChild();
37791 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37792 this.add(region, ret);
37797 // create a new Layout (which is a Border Layout...
37799 var clayout = cfg.layout;
37800 clayout.el = this.el.createChild();
37801 clayout.items = clayout.items || [];
37805 // replace this exitems with the clayout ones..
37806 xitems = clayout.items;
37808 // force background off if it's in center...
37809 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37810 cfg.background = false;
37812 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37815 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37816 //console.log('adding nested layout panel ' + cfg.toSource());
37817 this.add(region, ret);
37818 nb = {}; /// find first...
37823 // needs grid and region
37825 //var el = this.getRegion(region).el.createChild();
37827 *var el = this.el.createChild();
37828 // create the grid first...
37829 cfg.grid.container = el;
37830 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37833 if (region == 'center' && this.active ) {
37834 cfg.background = false;
37837 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37839 this.add(region, ret);
37841 if (cfg.background) {
37842 // render grid on panel activation (if panel background)
37843 ret.on('activate', function(gp) {
37844 if (!gp.grid.rendered) {
37845 // gp.grid.render(el);
37849 // cfg.grid.render(el);
37855 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37856 // it was the old xcomponent building that caused this before.
37857 // espeically if border is the top element in the tree.
37867 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37869 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37870 this.add(region, ret);
37874 throw "Can not add '" + cfg.xtype + "' to Border";
37880 this.beginUpdate();
37884 Roo.each(xitems, function(i) {
37885 region = nb && i.region ? i.region : false;
37887 var add = ret.addxtype(i);
37890 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37891 if (!i.background) {
37892 abn[region] = nb[region] ;
37899 // make the last non-background panel active..
37900 //if (nb) { Roo.log(abn); }
37903 for(var r in abn) {
37904 region = this.getRegion(r);
37906 // tried using nb[r], but it does not work..
37908 region.showPanel(abn[r]);
37919 factory : function(cfg)
37922 var validRegions = Roo.bootstrap.layout.Border.regions;
37924 var target = cfg.region;
37927 var r = Roo.bootstrap.layout;
37931 return new r.North(cfg);
37933 return new r.South(cfg);
37935 return new r.East(cfg);
37937 return new r.West(cfg);
37939 return new r.Center(cfg);
37941 throw 'Layout region "'+target+'" not supported.';
37948 * Ext JS Library 1.1.1
37949 * Copyright(c) 2006-2007, Ext JS, LLC.
37951 * Originally Released Under LGPL - original licence link has changed is not relivant.
37954 * <script type="text/javascript">
37958 * @class Roo.bootstrap.layout.Basic
37959 * @extends Roo.util.Observable
37960 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37961 * and does not have a titlebar, tabs or any other features. All it does is size and position
37962 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37963 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37964 * @cfg {string} region the region that it inhabits..
37965 * @cfg {bool} skipConfig skip config?
37969 Roo.bootstrap.layout.Basic = function(config){
37971 this.mgr = config.mgr;
37973 this.position = config.region;
37975 var skipConfig = config.skipConfig;
37979 * @scope Roo.BasicLayoutRegion
37983 * @event beforeremove
37984 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37985 * @param {Roo.LayoutRegion} this
37986 * @param {Roo.ContentPanel} panel The panel
37987 * @param {Object} e The cancel event object
37989 "beforeremove" : true,
37991 * @event invalidated
37992 * Fires when the layout for this region is changed.
37993 * @param {Roo.LayoutRegion} this
37995 "invalidated" : true,
37997 * @event visibilitychange
37998 * Fires when this region is shown or hidden
37999 * @param {Roo.LayoutRegion} this
38000 * @param {Boolean} visibility true or false
38002 "visibilitychange" : true,
38004 * @event paneladded
38005 * Fires when a panel is added.
38006 * @param {Roo.LayoutRegion} this
38007 * @param {Roo.ContentPanel} panel The panel
38009 "paneladded" : true,
38011 * @event panelremoved
38012 * Fires when a panel is removed.
38013 * @param {Roo.LayoutRegion} this
38014 * @param {Roo.ContentPanel} panel The panel
38016 "panelremoved" : true,
38018 * @event beforecollapse
38019 * Fires when this region before collapse.
38020 * @param {Roo.LayoutRegion} this
38022 "beforecollapse" : true,
38025 * Fires when this region is collapsed.
38026 * @param {Roo.LayoutRegion} this
38028 "collapsed" : true,
38031 * Fires when this region is expanded.
38032 * @param {Roo.LayoutRegion} this
38037 * Fires when this region is slid into view.
38038 * @param {Roo.LayoutRegion} this
38040 "slideshow" : true,
38043 * Fires when this region slides out of view.
38044 * @param {Roo.LayoutRegion} this
38046 "slidehide" : true,
38048 * @event panelactivated
38049 * Fires when a panel is activated.
38050 * @param {Roo.LayoutRegion} this
38051 * @param {Roo.ContentPanel} panel The activated panel
38053 "panelactivated" : true,
38056 * Fires when the user resizes this region.
38057 * @param {Roo.LayoutRegion} this
38058 * @param {Number} newSize The new size (width for east/west, height for north/south)
38062 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38063 this.panels = new Roo.util.MixedCollection();
38064 this.panels.getKey = this.getPanelId.createDelegate(this);
38066 this.activePanel = null;
38067 // ensure listeners are added...
38069 if (config.listeners || config.events) {
38070 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38071 listeners : config.listeners || {},
38072 events : config.events || {}
38076 if(skipConfig !== true){
38077 this.applyConfig(config);
38081 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38083 getPanelId : function(p){
38087 applyConfig : function(config){
38088 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38089 this.config = config;
38094 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38095 * the width, for horizontal (north, south) the height.
38096 * @param {Number} newSize The new width or height
38098 resizeTo : function(newSize){
38099 var el = this.el ? this.el :
38100 (this.activePanel ? this.activePanel.getEl() : null);
38102 switch(this.position){
38105 el.setWidth(newSize);
38106 this.fireEvent("resized", this, newSize);
38110 el.setHeight(newSize);
38111 this.fireEvent("resized", this, newSize);
38117 getBox : function(){
38118 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38121 getMargins : function(){
38122 return this.margins;
38125 updateBox : function(box){
38127 var el = this.activePanel.getEl();
38128 el.dom.style.left = box.x + "px";
38129 el.dom.style.top = box.y + "px";
38130 this.activePanel.setSize(box.width, box.height);
38134 * Returns the container element for this region.
38135 * @return {Roo.Element}
38137 getEl : function(){
38138 return this.activePanel;
38142 * Returns true if this region is currently visible.
38143 * @return {Boolean}
38145 isVisible : function(){
38146 return this.activePanel ? true : false;
38149 setActivePanel : function(panel){
38150 panel = this.getPanel(panel);
38151 if(this.activePanel && this.activePanel != panel){
38152 this.activePanel.setActiveState(false);
38153 this.activePanel.getEl().setLeftTop(-10000,-10000);
38155 this.activePanel = panel;
38156 panel.setActiveState(true);
38158 panel.setSize(this.box.width, this.box.height);
38160 this.fireEvent("panelactivated", this, panel);
38161 this.fireEvent("invalidated");
38165 * Show the specified panel.
38166 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38167 * @return {Roo.ContentPanel} The shown panel or null
38169 showPanel : function(panel){
38170 panel = this.getPanel(panel);
38172 this.setActivePanel(panel);
38178 * Get the active panel for this region.
38179 * @return {Roo.ContentPanel} The active panel or null
38181 getActivePanel : function(){
38182 return this.activePanel;
38186 * Add the passed ContentPanel(s)
38187 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38188 * @return {Roo.ContentPanel} The panel added (if only one was added)
38190 add : function(panel){
38191 if(arguments.length > 1){
38192 for(var i = 0, len = arguments.length; i < len; i++) {
38193 this.add(arguments[i]);
38197 if(this.hasPanel(panel)){
38198 this.showPanel(panel);
38201 var el = panel.getEl();
38202 if(el.dom.parentNode != this.mgr.el.dom){
38203 this.mgr.el.dom.appendChild(el.dom);
38205 if(panel.setRegion){
38206 panel.setRegion(this);
38208 this.panels.add(panel);
38209 el.setStyle("position", "absolute");
38210 if(!panel.background){
38211 this.setActivePanel(panel);
38212 if(this.config.initialSize && this.panels.getCount()==1){
38213 this.resizeTo(this.config.initialSize);
38216 this.fireEvent("paneladded", this, panel);
38221 * Returns true if the panel is in this region.
38222 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38223 * @return {Boolean}
38225 hasPanel : function(panel){
38226 if(typeof panel == "object"){ // must be panel obj
38227 panel = panel.getId();
38229 return this.getPanel(panel) ? true : false;
38233 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38234 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38235 * @param {Boolean} preservePanel Overrides the config preservePanel option
38236 * @return {Roo.ContentPanel} The panel that was removed
38238 remove : function(panel, preservePanel){
38239 panel = this.getPanel(panel);
38244 this.fireEvent("beforeremove", this, panel, e);
38245 if(e.cancel === true){
38248 var panelId = panel.getId();
38249 this.panels.removeKey(panelId);
38254 * Returns the panel specified or null if it's not in this region.
38255 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38256 * @return {Roo.ContentPanel}
38258 getPanel : function(id){
38259 if(typeof id == "object"){ // must be panel obj
38262 return this.panels.get(id);
38266 * Returns this regions position (north/south/east/west/center).
38269 getPosition: function(){
38270 return this.position;
38274 * Ext JS Library 1.1.1
38275 * Copyright(c) 2006-2007, Ext JS, LLC.
38277 * Originally Released Under LGPL - original licence link has changed is not relivant.
38280 * <script type="text/javascript">
38284 * @class Roo.bootstrap.layout.Region
38285 * @extends Roo.bootstrap.layout.Basic
38286 * This class represents a region in a layout manager.
38288 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38289 * @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})
38290 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38291 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38292 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38293 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38294 * @cfg {String} title The title for the region (overrides panel titles)
38295 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38296 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38297 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38298 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38299 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38300 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38301 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38302 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38303 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38304 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38306 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38307 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38308 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38309 * @cfg {Number} width For East/West panels
38310 * @cfg {Number} height For North/South panels
38311 * @cfg {Boolean} split To show the splitter
38312 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38314 * @cfg {string} cls Extra CSS classes to add to region
38316 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38317 * @cfg {string} region the region that it inhabits..
38320 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38321 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38323 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38324 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38325 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38327 Roo.bootstrap.layout.Region = function(config)
38329 this.applyConfig(config);
38331 var mgr = config.mgr;
38332 var pos = config.region;
38333 config.skipConfig = true;
38334 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38337 this.onRender(mgr.el);
38340 this.visible = true;
38341 this.collapsed = false;
38342 this.unrendered_panels = [];
38345 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38347 position: '', // set by wrapper (eg. north/south etc..)
38348 unrendered_panels : null, // unrendered panels.
38350 tabPosition : false,
38352 mgr: false, // points to 'Border'
38355 createBody : function(){
38356 /** This region's body element
38357 * @type Roo.Element */
38358 this.bodyEl = this.el.createChild({
38360 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38364 onRender: function(ctr, pos)
38366 var dh = Roo.DomHelper;
38367 /** This region's container element
38368 * @type Roo.Element */
38369 this.el = dh.append(ctr.dom, {
38371 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38373 /** This region's title element
38374 * @type Roo.Element */
38376 this.titleEl = dh.append(this.el.dom, {
38378 unselectable: "on",
38379 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38381 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38382 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38386 this.titleEl.enableDisplayMode();
38387 /** This region's title text element
38388 * @type HTMLElement */
38389 this.titleTextEl = this.titleEl.dom.firstChild;
38390 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38392 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38393 this.closeBtn.enableDisplayMode();
38394 this.closeBtn.on("click", this.closeClicked, this);
38395 this.closeBtn.hide();
38397 this.createBody(this.config);
38398 if(this.config.hideWhenEmpty){
38400 this.on("paneladded", this.validateVisibility, this);
38401 this.on("panelremoved", this.validateVisibility, this);
38403 if(this.autoScroll){
38404 this.bodyEl.setStyle("overflow", "auto");
38406 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38408 //if(c.titlebar !== false){
38409 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38410 this.titleEl.hide();
38412 this.titleEl.show();
38413 if(this.config.title){
38414 this.titleTextEl.innerHTML = this.config.title;
38418 if(this.config.collapsed){
38419 this.collapse(true);
38421 if(this.config.hidden){
38425 if (this.unrendered_panels && this.unrendered_panels.length) {
38426 for (var i =0;i< this.unrendered_panels.length; i++) {
38427 this.add(this.unrendered_panels[i]);
38429 this.unrendered_panels = null;
38435 applyConfig : function(c)
38438 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38439 var dh = Roo.DomHelper;
38440 if(c.titlebar !== false){
38441 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38442 this.collapseBtn.on("click", this.collapse, this);
38443 this.collapseBtn.enableDisplayMode();
38445 if(c.showPin === true || this.showPin){
38446 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38447 this.stickBtn.enableDisplayMode();
38448 this.stickBtn.on("click", this.expand, this);
38449 this.stickBtn.hide();
38454 /** This region's collapsed element
38455 * @type Roo.Element */
38458 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38459 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38462 if(c.floatable !== false){
38463 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38464 this.collapsedEl.on("click", this.collapseClick, this);
38467 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38468 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38469 id: "message", unselectable: "on", style:{"float":"left"}});
38470 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38472 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38473 this.expandBtn.on("click", this.expand, this);
38477 if(this.collapseBtn){
38478 this.collapseBtn.setVisible(c.collapsible == true);
38481 this.cmargins = c.cmargins || this.cmargins ||
38482 (this.position == "west" || this.position == "east" ?
38483 {top: 0, left: 2, right:2, bottom: 0} :
38484 {top: 2, left: 0, right:0, bottom: 2});
38486 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38489 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38491 this.autoScroll = c.autoScroll || false;
38496 this.duration = c.duration || .30;
38497 this.slideDuration = c.slideDuration || .45;
38502 * Returns true if this region is currently visible.
38503 * @return {Boolean}
38505 isVisible : function(){
38506 return this.visible;
38510 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38511 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38513 //setCollapsedTitle : function(title){
38514 // title = title || " ";
38515 // if(this.collapsedTitleTextEl){
38516 // this.collapsedTitleTextEl.innerHTML = title;
38520 getBox : function(){
38522 // if(!this.collapsed){
38523 b = this.el.getBox(false, true);
38525 // b = this.collapsedEl.getBox(false, true);
38530 getMargins : function(){
38531 return this.margins;
38532 //return this.collapsed ? this.cmargins : this.margins;
38535 highlight : function(){
38536 this.el.addClass("x-layout-panel-dragover");
38539 unhighlight : function(){
38540 this.el.removeClass("x-layout-panel-dragover");
38543 updateBox : function(box)
38545 if (!this.bodyEl) {
38546 return; // not rendered yet..
38550 if(!this.collapsed){
38551 this.el.dom.style.left = box.x + "px";
38552 this.el.dom.style.top = box.y + "px";
38553 this.updateBody(box.width, box.height);
38555 this.collapsedEl.dom.style.left = box.x + "px";
38556 this.collapsedEl.dom.style.top = box.y + "px";
38557 this.collapsedEl.setSize(box.width, box.height);
38560 this.tabs.autoSizeTabs();
38564 updateBody : function(w, h)
38567 this.el.setWidth(w);
38568 w -= this.el.getBorderWidth("rl");
38569 if(this.config.adjustments){
38570 w += this.config.adjustments[0];
38573 if(h !== null && h > 0){
38574 this.el.setHeight(h);
38575 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38576 h -= this.el.getBorderWidth("tb");
38577 if(this.config.adjustments){
38578 h += this.config.adjustments[1];
38580 this.bodyEl.setHeight(h);
38582 h = this.tabs.syncHeight(h);
38585 if(this.panelSize){
38586 w = w !== null ? w : this.panelSize.width;
38587 h = h !== null ? h : this.panelSize.height;
38589 if(this.activePanel){
38590 var el = this.activePanel.getEl();
38591 w = w !== null ? w : el.getWidth();
38592 h = h !== null ? h : el.getHeight();
38593 this.panelSize = {width: w, height: h};
38594 this.activePanel.setSize(w, h);
38596 if(Roo.isIE && this.tabs){
38597 this.tabs.el.repaint();
38602 * Returns the container element for this region.
38603 * @return {Roo.Element}
38605 getEl : function(){
38610 * Hides this region.
38613 //if(!this.collapsed){
38614 this.el.dom.style.left = "-2000px";
38617 // this.collapsedEl.dom.style.left = "-2000px";
38618 // this.collapsedEl.hide();
38620 this.visible = false;
38621 this.fireEvent("visibilitychange", this, false);
38625 * Shows this region if it was previously hidden.
38628 //if(!this.collapsed){
38631 // this.collapsedEl.show();
38633 this.visible = true;
38634 this.fireEvent("visibilitychange", this, true);
38637 closeClicked : function(){
38638 if(this.activePanel){
38639 this.remove(this.activePanel);
38643 collapseClick : function(e){
38645 e.stopPropagation();
38648 e.stopPropagation();
38654 * Collapses this region.
38655 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38658 collapse : function(skipAnim, skipCheck = false){
38659 if(this.collapsed) {
38663 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38665 this.collapsed = true;
38667 this.split.el.hide();
38669 if(this.config.animate && skipAnim !== true){
38670 this.fireEvent("invalidated", this);
38671 this.animateCollapse();
38673 this.el.setLocation(-20000,-20000);
38675 this.collapsedEl.show();
38676 this.fireEvent("collapsed", this);
38677 this.fireEvent("invalidated", this);
38683 animateCollapse : function(){
38688 * Expands this region if it was previously collapsed.
38689 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38690 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38693 expand : function(e, skipAnim){
38695 e.stopPropagation();
38697 if(!this.collapsed || this.el.hasActiveFx()) {
38701 this.afterSlideIn();
38704 this.collapsed = false;
38705 if(this.config.animate && skipAnim !== true){
38706 this.animateExpand();
38710 this.split.el.show();
38712 this.collapsedEl.setLocation(-2000,-2000);
38713 this.collapsedEl.hide();
38714 this.fireEvent("invalidated", this);
38715 this.fireEvent("expanded", this);
38719 animateExpand : function(){
38723 initTabs : function()
38725 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38727 var ts = new Roo.bootstrap.panel.Tabs({
38728 el: this.bodyEl.dom,
38730 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38731 disableTooltips: this.config.disableTabTips,
38732 toolbar : this.config.toolbar
38735 if(this.config.hideTabs){
38736 ts.stripWrap.setDisplayed(false);
38739 ts.resizeTabs = this.config.resizeTabs === true;
38740 ts.minTabWidth = this.config.minTabWidth || 40;
38741 ts.maxTabWidth = this.config.maxTabWidth || 250;
38742 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38743 ts.monitorResize = false;
38744 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38745 ts.bodyEl.addClass('roo-layout-tabs-body');
38746 this.panels.each(this.initPanelAsTab, this);
38749 initPanelAsTab : function(panel){
38750 var ti = this.tabs.addTab(
38754 this.config.closeOnTab && panel.isClosable(),
38757 if(panel.tabTip !== undefined){
38758 ti.setTooltip(panel.tabTip);
38760 ti.on("activate", function(){
38761 this.setActivePanel(panel);
38764 if(this.config.closeOnTab){
38765 ti.on("beforeclose", function(t, e){
38767 this.remove(panel);
38771 panel.tabItem = ti;
38776 updatePanelTitle : function(panel, title)
38778 if(this.activePanel == panel){
38779 this.updateTitle(title);
38782 var ti = this.tabs.getTab(panel.getEl().id);
38784 if(panel.tabTip !== undefined){
38785 ti.setTooltip(panel.tabTip);
38790 updateTitle : function(title){
38791 if(this.titleTextEl && !this.config.title){
38792 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38796 setActivePanel : function(panel)
38798 panel = this.getPanel(panel);
38799 if(this.activePanel && this.activePanel != panel){
38800 if(this.activePanel.setActiveState(false) === false){
38804 this.activePanel = panel;
38805 panel.setActiveState(true);
38806 if(this.panelSize){
38807 panel.setSize(this.panelSize.width, this.panelSize.height);
38810 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38812 this.updateTitle(panel.getTitle());
38814 this.fireEvent("invalidated", this);
38816 this.fireEvent("panelactivated", this, panel);
38820 * Shows the specified panel.
38821 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38822 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38824 showPanel : function(panel)
38826 panel = this.getPanel(panel);
38829 var tab = this.tabs.getTab(panel.getEl().id);
38830 if(tab.isHidden()){
38831 this.tabs.unhideTab(tab.id);
38835 this.setActivePanel(panel);
38842 * Get the active panel for this region.
38843 * @return {Roo.ContentPanel} The active panel or null
38845 getActivePanel : function(){
38846 return this.activePanel;
38849 validateVisibility : function(){
38850 if(this.panels.getCount() < 1){
38851 this.updateTitle(" ");
38852 this.closeBtn.hide();
38855 if(!this.isVisible()){
38862 * Adds the passed ContentPanel(s) to this region.
38863 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38864 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38866 add : function(panel)
38868 if(arguments.length > 1){
38869 for(var i = 0, len = arguments.length; i < len; i++) {
38870 this.add(arguments[i]);
38875 // if we have not been rendered yet, then we can not really do much of this..
38876 if (!this.bodyEl) {
38877 this.unrendered_panels.push(panel);
38884 if(this.hasPanel(panel)){
38885 this.showPanel(panel);
38888 panel.setRegion(this);
38889 this.panels.add(panel);
38890 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38891 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38892 // and hide them... ???
38893 this.bodyEl.dom.appendChild(panel.getEl().dom);
38894 if(panel.background !== true){
38895 this.setActivePanel(panel);
38897 this.fireEvent("paneladded", this, panel);
38904 this.initPanelAsTab(panel);
38908 if(panel.background !== true){
38909 this.tabs.activate(panel.getEl().id);
38911 this.fireEvent("paneladded", this, panel);
38916 * Hides the tab for the specified panel.
38917 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38919 hidePanel : function(panel){
38920 if(this.tabs && (panel = this.getPanel(panel))){
38921 this.tabs.hideTab(panel.getEl().id);
38926 * Unhides the tab for a previously hidden panel.
38927 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38929 unhidePanel : function(panel){
38930 if(this.tabs && (panel = this.getPanel(panel))){
38931 this.tabs.unhideTab(panel.getEl().id);
38935 clearPanels : function(){
38936 while(this.panels.getCount() > 0){
38937 this.remove(this.panels.first());
38942 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38943 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38944 * @param {Boolean} preservePanel Overrides the config preservePanel option
38945 * @return {Roo.ContentPanel} The panel that was removed
38947 remove : function(panel, preservePanel)
38949 panel = this.getPanel(panel);
38954 this.fireEvent("beforeremove", this, panel, e);
38955 if(e.cancel === true){
38958 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38959 var panelId = panel.getId();
38960 this.panels.removeKey(panelId);
38962 document.body.appendChild(panel.getEl().dom);
38965 this.tabs.removeTab(panel.getEl().id);
38966 }else if (!preservePanel){
38967 this.bodyEl.dom.removeChild(panel.getEl().dom);
38969 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38970 var p = this.panels.first();
38971 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38972 tempEl.appendChild(p.getEl().dom);
38973 this.bodyEl.update("");
38974 this.bodyEl.dom.appendChild(p.getEl().dom);
38976 this.updateTitle(p.getTitle());
38978 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38979 this.setActivePanel(p);
38981 panel.setRegion(null);
38982 if(this.activePanel == panel){
38983 this.activePanel = null;
38985 if(this.config.autoDestroy !== false && preservePanel !== true){
38986 try{panel.destroy();}catch(e){}
38988 this.fireEvent("panelremoved", this, panel);
38993 * Returns the TabPanel component used by this region
38994 * @return {Roo.TabPanel}
38996 getTabs : function(){
39000 createTool : function(parentEl, className){
39001 var btn = Roo.DomHelper.append(parentEl, {
39003 cls: "x-layout-tools-button",
39006 cls: "roo-layout-tools-button-inner " + className,
39010 btn.addClassOnOver("roo-layout-tools-button-over");
39015 * Ext JS Library 1.1.1
39016 * Copyright(c) 2006-2007, Ext JS, LLC.
39018 * Originally Released Under LGPL - original licence link has changed is not relivant.
39021 * <script type="text/javascript">
39027 * @class Roo.SplitLayoutRegion
39028 * @extends Roo.LayoutRegion
39029 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39031 Roo.bootstrap.layout.Split = function(config){
39032 this.cursor = config.cursor;
39033 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39036 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39038 splitTip : "Drag to resize.",
39039 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39040 useSplitTips : false,
39042 applyConfig : function(config){
39043 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39046 onRender : function(ctr,pos) {
39048 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39049 if(!this.config.split){
39054 var splitEl = Roo.DomHelper.append(ctr.dom, {
39056 id: this.el.id + "-split",
39057 cls: "roo-layout-split roo-layout-split-"+this.position,
39060 /** The SplitBar for this region
39061 * @type Roo.SplitBar */
39062 // does not exist yet...
39063 Roo.log([this.position, this.orientation]);
39065 this.split = new Roo.bootstrap.SplitBar({
39066 dragElement : splitEl,
39067 resizingElement: this.el,
39068 orientation : this.orientation
39071 this.split.on("moved", this.onSplitMove, this);
39072 this.split.useShim = this.config.useShim === true;
39073 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39074 if(this.useSplitTips){
39075 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39077 //if(config.collapsible){
39078 // this.split.el.on("dblclick", this.collapse, this);
39081 if(typeof this.config.minSize != "undefined"){
39082 this.split.minSize = this.config.minSize;
39084 if(typeof this.config.maxSize != "undefined"){
39085 this.split.maxSize = this.config.maxSize;
39087 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39088 this.hideSplitter();
39093 getHMaxSize : function(){
39094 var cmax = this.config.maxSize || 10000;
39095 var center = this.mgr.getRegion("center");
39096 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39099 getVMaxSize : function(){
39100 var cmax = this.config.maxSize || 10000;
39101 var center = this.mgr.getRegion("center");
39102 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39105 onSplitMove : function(split, newSize){
39106 this.fireEvent("resized", this, newSize);
39110 * Returns the {@link Roo.SplitBar} for this region.
39111 * @return {Roo.SplitBar}
39113 getSplitBar : function(){
39118 this.hideSplitter();
39119 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39122 hideSplitter : function(){
39124 this.split.el.setLocation(-2000,-2000);
39125 this.split.el.hide();
39131 this.split.el.show();
39133 Roo.bootstrap.layout.Split.superclass.show.call(this);
39136 beforeSlide: function(){
39137 if(Roo.isGecko){// firefox overflow auto bug workaround
39138 this.bodyEl.clip();
39140 this.tabs.bodyEl.clip();
39142 if(this.activePanel){
39143 this.activePanel.getEl().clip();
39145 if(this.activePanel.beforeSlide){
39146 this.activePanel.beforeSlide();
39152 afterSlide : function(){
39153 if(Roo.isGecko){// firefox overflow auto bug workaround
39154 this.bodyEl.unclip();
39156 this.tabs.bodyEl.unclip();
39158 if(this.activePanel){
39159 this.activePanel.getEl().unclip();
39160 if(this.activePanel.afterSlide){
39161 this.activePanel.afterSlide();
39167 initAutoHide : function(){
39168 if(this.autoHide !== false){
39169 if(!this.autoHideHd){
39170 var st = new Roo.util.DelayedTask(this.slideIn, this);
39171 this.autoHideHd = {
39172 "mouseout": function(e){
39173 if(!e.within(this.el, true)){
39177 "mouseover" : function(e){
39183 this.el.on(this.autoHideHd);
39187 clearAutoHide : function(){
39188 if(this.autoHide !== false){
39189 this.el.un("mouseout", this.autoHideHd.mouseout);
39190 this.el.un("mouseover", this.autoHideHd.mouseover);
39194 clearMonitor : function(){
39195 Roo.get(document).un("click", this.slideInIf, this);
39198 // these names are backwards but not changed for compat
39199 slideOut : function(){
39200 if(this.isSlid || this.el.hasActiveFx()){
39203 this.isSlid = true;
39204 if(this.collapseBtn){
39205 this.collapseBtn.hide();
39207 this.closeBtnState = this.closeBtn.getStyle('display');
39208 this.closeBtn.hide();
39210 this.stickBtn.show();
39213 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39214 this.beforeSlide();
39215 this.el.setStyle("z-index", 10001);
39216 this.el.slideIn(this.getSlideAnchor(), {
39217 callback: function(){
39219 this.initAutoHide();
39220 Roo.get(document).on("click", this.slideInIf, this);
39221 this.fireEvent("slideshow", this);
39228 afterSlideIn : function(){
39229 this.clearAutoHide();
39230 this.isSlid = false;
39231 this.clearMonitor();
39232 this.el.setStyle("z-index", "");
39233 if(this.collapseBtn){
39234 this.collapseBtn.show();
39236 this.closeBtn.setStyle('display', this.closeBtnState);
39238 this.stickBtn.hide();
39240 this.fireEvent("slidehide", this);
39243 slideIn : function(cb){
39244 if(!this.isSlid || this.el.hasActiveFx()){
39248 this.isSlid = false;
39249 this.beforeSlide();
39250 this.el.slideOut(this.getSlideAnchor(), {
39251 callback: function(){
39252 this.el.setLeftTop(-10000, -10000);
39254 this.afterSlideIn();
39262 slideInIf : function(e){
39263 if(!e.within(this.el)){
39268 animateCollapse : function(){
39269 this.beforeSlide();
39270 this.el.setStyle("z-index", 20000);
39271 var anchor = this.getSlideAnchor();
39272 this.el.slideOut(anchor, {
39273 callback : function(){
39274 this.el.setStyle("z-index", "");
39275 this.collapsedEl.slideIn(anchor, {duration:.3});
39277 this.el.setLocation(-10000,-10000);
39279 this.fireEvent("collapsed", this);
39286 animateExpand : function(){
39287 this.beforeSlide();
39288 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39289 this.el.setStyle("z-index", 20000);
39290 this.collapsedEl.hide({
39293 this.el.slideIn(this.getSlideAnchor(), {
39294 callback : function(){
39295 this.el.setStyle("z-index", "");
39298 this.split.el.show();
39300 this.fireEvent("invalidated", this);
39301 this.fireEvent("expanded", this);
39329 getAnchor : function(){
39330 return this.anchors[this.position];
39333 getCollapseAnchor : function(){
39334 return this.canchors[this.position];
39337 getSlideAnchor : function(){
39338 return this.sanchors[this.position];
39341 getAlignAdj : function(){
39342 var cm = this.cmargins;
39343 switch(this.position){
39359 getExpandAdj : function(){
39360 var c = this.collapsedEl, cm = this.cmargins;
39361 switch(this.position){
39363 return [-(cm.right+c.getWidth()+cm.left), 0];
39366 return [cm.right+c.getWidth()+cm.left, 0];
39369 return [0, -(cm.top+cm.bottom+c.getHeight())];
39372 return [0, cm.top+cm.bottom+c.getHeight()];
39378 * Ext JS Library 1.1.1
39379 * Copyright(c) 2006-2007, Ext JS, LLC.
39381 * Originally Released Under LGPL - original licence link has changed is not relivant.
39384 * <script type="text/javascript">
39387 * These classes are private internal classes
39389 Roo.bootstrap.layout.Center = function(config){
39390 config.region = "center";
39391 Roo.bootstrap.layout.Region.call(this, config);
39392 this.visible = true;
39393 this.minWidth = config.minWidth || 20;
39394 this.minHeight = config.minHeight || 20;
39397 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39399 // center panel can't be hidden
39403 // center panel can't be hidden
39406 getMinWidth: function(){
39407 return this.minWidth;
39410 getMinHeight: function(){
39411 return this.minHeight;
39425 Roo.bootstrap.layout.North = function(config)
39427 config.region = 'north';
39428 config.cursor = 'n-resize';
39430 Roo.bootstrap.layout.Split.call(this, config);
39434 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39435 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39436 this.split.el.addClass("roo-layout-split-v");
39438 //var size = config.initialSize || config.height;
39439 //if(this.el && typeof size != "undefined"){
39440 // this.el.setHeight(size);
39443 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39445 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39448 onRender : function(ctr, pos)
39450 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39451 var size = this.config.initialSize || this.config.height;
39452 if(this.el && typeof size != "undefined"){
39453 this.el.setHeight(size);
39458 getBox : function(){
39459 if(this.collapsed){
39460 return this.collapsedEl.getBox();
39462 var box = this.el.getBox();
39464 box.height += this.split.el.getHeight();
39469 updateBox : function(box){
39470 if(this.split && !this.collapsed){
39471 box.height -= this.split.el.getHeight();
39472 this.split.el.setLeft(box.x);
39473 this.split.el.setTop(box.y+box.height);
39474 this.split.el.setWidth(box.width);
39476 if(this.collapsed){
39477 this.updateBody(box.width, null);
39479 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39487 Roo.bootstrap.layout.South = function(config){
39488 config.region = 'south';
39489 config.cursor = 's-resize';
39490 Roo.bootstrap.layout.Split.call(this, config);
39492 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39493 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39494 this.split.el.addClass("roo-layout-split-v");
39499 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39500 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39502 onRender : function(ctr, pos)
39504 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39505 var size = this.config.initialSize || this.config.height;
39506 if(this.el && typeof size != "undefined"){
39507 this.el.setHeight(size);
39512 getBox : function(){
39513 if(this.collapsed){
39514 return this.collapsedEl.getBox();
39516 var box = this.el.getBox();
39518 var sh = this.split.el.getHeight();
39525 updateBox : function(box){
39526 if(this.split && !this.collapsed){
39527 var sh = this.split.el.getHeight();
39530 this.split.el.setLeft(box.x);
39531 this.split.el.setTop(box.y-sh);
39532 this.split.el.setWidth(box.width);
39534 if(this.collapsed){
39535 this.updateBody(box.width, null);
39537 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39541 Roo.bootstrap.layout.East = function(config){
39542 config.region = "east";
39543 config.cursor = "e-resize";
39544 Roo.bootstrap.layout.Split.call(this, config);
39546 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39547 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39548 this.split.el.addClass("roo-layout-split-h");
39552 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39553 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39555 onRender : function(ctr, pos)
39557 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39558 var size = this.config.initialSize || this.config.width;
39559 if(this.el && typeof size != "undefined"){
39560 this.el.setWidth(size);
39565 getBox : function(){
39566 if(this.collapsed){
39567 return this.collapsedEl.getBox();
39569 var box = this.el.getBox();
39571 var sw = this.split.el.getWidth();
39578 updateBox : function(box){
39579 if(this.split && !this.collapsed){
39580 var sw = this.split.el.getWidth();
39582 this.split.el.setLeft(box.x);
39583 this.split.el.setTop(box.y);
39584 this.split.el.setHeight(box.height);
39587 if(this.collapsed){
39588 this.updateBody(null, box.height);
39590 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39594 Roo.bootstrap.layout.West = function(config){
39595 config.region = "west";
39596 config.cursor = "w-resize";
39598 Roo.bootstrap.layout.Split.call(this, config);
39600 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39601 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39602 this.split.el.addClass("roo-layout-split-h");
39606 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39607 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39609 onRender: function(ctr, pos)
39611 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39612 var size = this.config.initialSize || this.config.width;
39613 if(typeof size != "undefined"){
39614 this.el.setWidth(size);
39618 getBox : function(){
39619 if(this.collapsed){
39620 return this.collapsedEl.getBox();
39622 var box = this.el.getBox();
39623 if (box.width == 0) {
39624 box.width = this.config.width; // kludge?
39627 box.width += this.split.el.getWidth();
39632 updateBox : function(box){
39633 if(this.split && !this.collapsed){
39634 var sw = this.split.el.getWidth();
39636 this.split.el.setLeft(box.x+box.width);
39637 this.split.el.setTop(box.y);
39638 this.split.el.setHeight(box.height);
39640 if(this.collapsed){
39641 this.updateBody(null, box.height);
39643 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39645 });Roo.namespace("Roo.bootstrap.panel");/*
39647 * Ext JS Library 1.1.1
39648 * Copyright(c) 2006-2007, Ext JS, LLC.
39650 * Originally Released Under LGPL - original licence link has changed is not relivant.
39653 * <script type="text/javascript">
39656 * @class Roo.ContentPanel
39657 * @extends Roo.util.Observable
39658 * A basic ContentPanel element.
39659 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39660 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39661 * @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
39662 * @cfg {Boolean} closable True if the panel can be closed/removed
39663 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39664 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39665 * @cfg {Toolbar} toolbar A toolbar for this panel
39666 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39667 * @cfg {String} title The title for this panel
39668 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39669 * @cfg {String} url Calls {@link #setUrl} with this value
39670 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39671 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39672 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39673 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39674 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39675 * @cfg {Boolean} badges render the badges
39676 * @cfg {String} cls extra classes to use
39677 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39680 * Create a new ContentPanel.
39681 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39682 * @param {String/Object} config A string to set only the title or a config object
39683 * @param {String} content (optional) Set the HTML content for this panel
39684 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39686 Roo.bootstrap.panel.Content = function( config){
39688 this.tpl = config.tpl || false;
39690 var el = config.el;
39691 var content = config.content;
39693 if(config.autoCreate){ // xtype is available if this is called from factory
39696 this.el = Roo.get(el);
39697 if(!this.el && config && config.autoCreate){
39698 if(typeof config.autoCreate == "object"){
39699 if(!config.autoCreate.id){
39700 config.autoCreate.id = config.id||el;
39702 this.el = Roo.DomHelper.append(document.body,
39703 config.autoCreate, true);
39707 cls: (config.cls || '') +
39708 (config.background ? ' bg-' + config.background : '') +
39709 " roo-layout-inactive-content",
39712 if (config.iframe) {
39716 style : 'border: 0px',
39717 src : 'about:blank'
39723 elcfg.html = config.html;
39727 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39728 if (config.iframe) {
39729 this.iframeEl = this.el.select('iframe',true).first();
39734 this.closable = false;
39735 this.loaded = false;
39736 this.active = false;
39739 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39741 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39743 this.wrapEl = this.el; //this.el.wrap();
39745 if (config.toolbar.items) {
39746 ti = config.toolbar.items ;
39747 delete config.toolbar.items ;
39751 this.toolbar.render(this.wrapEl, 'before');
39752 for(var i =0;i < ti.length;i++) {
39753 // Roo.log(['add child', items[i]]);
39754 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39756 this.toolbar.items = nitems;
39757 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39758 delete config.toolbar;
39762 // xtype created footer. - not sure if will work as we normally have to render first..
39763 if (this.footer && !this.footer.el && this.footer.xtype) {
39764 if (!this.wrapEl) {
39765 this.wrapEl = this.el.wrap();
39768 this.footer.container = this.wrapEl.createChild();
39770 this.footer = Roo.factory(this.footer, Roo);
39775 if(typeof config == "string"){
39776 this.title = config;
39778 Roo.apply(this, config);
39782 this.resizeEl = Roo.get(this.resizeEl, true);
39784 this.resizeEl = this.el;
39786 // handle view.xtype
39794 * Fires when this panel is activated.
39795 * @param {Roo.ContentPanel} this
39799 * @event deactivate
39800 * Fires when this panel is activated.
39801 * @param {Roo.ContentPanel} this
39803 "deactivate" : true,
39807 * Fires when this panel is resized if fitToFrame is true.
39808 * @param {Roo.ContentPanel} this
39809 * @param {Number} width The width after any component adjustments
39810 * @param {Number} height The height after any component adjustments
39816 * Fires when this tab is created
39817 * @param {Roo.ContentPanel} this
39828 if(this.autoScroll && !this.iframe){
39829 this.resizeEl.setStyle("overflow", "auto");
39831 // fix randome scrolling
39832 //this.el.on('scroll', function() {
39833 // Roo.log('fix random scolling');
39834 // this.scrollTo('top',0);
39837 content = content || this.content;
39839 this.setContent(content);
39841 if(config && config.url){
39842 this.setUrl(this.url, this.params, this.loadOnce);
39847 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39849 if (this.view && typeof(this.view.xtype) != 'undefined') {
39850 this.view.el = this.el.appendChild(document.createElement("div"));
39851 this.view = Roo.factory(this.view);
39852 this.view.render && this.view.render(false, '');
39856 this.fireEvent('render', this);
39859 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39869 setRegion : function(region){
39870 this.region = region;
39871 this.setActiveClass(region && !this.background);
39875 setActiveClass: function(state)
39878 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39879 this.el.setStyle('position','relative');
39881 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39882 this.el.setStyle('position', 'absolute');
39887 * Returns the toolbar for this Panel if one was configured.
39888 * @return {Roo.Toolbar}
39890 getToolbar : function(){
39891 return this.toolbar;
39894 setActiveState : function(active)
39896 this.active = active;
39897 this.setActiveClass(active);
39899 if(this.fireEvent("deactivate", this) === false){
39904 this.fireEvent("activate", this);
39908 * Updates this panel's element (not for iframe)
39909 * @param {String} content The new content
39910 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39912 setContent : function(content, loadScripts){
39917 this.el.update(content, loadScripts);
39920 ignoreResize : function(w, h){
39921 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39924 this.lastSize = {width: w, height: h};
39929 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39930 * @return {Roo.UpdateManager} The UpdateManager
39932 getUpdateManager : function(){
39936 return this.el.getUpdateManager();
39939 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39940 * Does not work with IFRAME contents
39941 * @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:
39944 url: "your-url.php",
39945 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39946 callback: yourFunction,
39947 scope: yourObject, //(optional scope)
39950 text: "Loading...",
39956 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39957 * 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.
39958 * @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}
39959 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39960 * @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.
39961 * @return {Roo.ContentPanel} this
39969 var um = this.el.getUpdateManager();
39970 um.update.apply(um, arguments);
39976 * 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.
39977 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39978 * @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)
39979 * @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)
39980 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39982 setUrl : function(url, params, loadOnce){
39984 this.iframeEl.dom.src = url;
39988 if(this.refreshDelegate){
39989 this.removeListener("activate", this.refreshDelegate);
39991 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39992 this.on("activate", this.refreshDelegate);
39993 return this.el.getUpdateManager();
39996 _handleRefresh : function(url, params, loadOnce){
39997 if(!loadOnce || !this.loaded){
39998 var updater = this.el.getUpdateManager();
39999 updater.update(url, params, this._setLoaded.createDelegate(this));
40003 _setLoaded : function(){
40004 this.loaded = true;
40008 * Returns this panel's id
40011 getId : function(){
40016 * Returns this panel's element - used by regiosn to add.
40017 * @return {Roo.Element}
40019 getEl : function(){
40020 return this.wrapEl || this.el;
40025 adjustForComponents : function(width, height)
40027 //Roo.log('adjustForComponents ');
40028 if(this.resizeEl != this.el){
40029 width -= this.el.getFrameWidth('lr');
40030 height -= this.el.getFrameWidth('tb');
40033 var te = this.toolbar.getEl();
40034 te.setWidth(width);
40035 height -= te.getHeight();
40038 var te = this.footer.getEl();
40039 te.setWidth(width);
40040 height -= te.getHeight();
40044 if(this.adjustments){
40045 width += this.adjustments[0];
40046 height += this.adjustments[1];
40048 return {"width": width, "height": height};
40051 setSize : function(width, height){
40052 if(this.fitToFrame && !this.ignoreResize(width, height)){
40053 if(this.fitContainer && this.resizeEl != this.el){
40054 this.el.setSize(width, height);
40056 var size = this.adjustForComponents(width, height);
40058 this.iframeEl.setSize(width,height);
40061 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40062 this.fireEvent('resize', this, size.width, size.height);
40069 * Returns this panel's title
40072 getTitle : function(){
40074 if (typeof(this.title) != 'object') {
40079 for (var k in this.title) {
40080 if (!this.title.hasOwnProperty(k)) {
40084 if (k.indexOf('-') >= 0) {
40085 var s = k.split('-');
40086 for (var i = 0; i<s.length; i++) {
40087 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40090 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40097 * Set this panel's title
40098 * @param {String} title
40100 setTitle : function(title){
40101 this.title = title;
40103 this.region.updatePanelTitle(this, title);
40108 * Returns true is this panel was configured to be closable
40109 * @return {Boolean}
40111 isClosable : function(){
40112 return this.closable;
40115 beforeSlide : function(){
40117 this.resizeEl.clip();
40120 afterSlide : function(){
40122 this.resizeEl.unclip();
40126 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40127 * Will fail silently if the {@link #setUrl} method has not been called.
40128 * This does not activate the panel, just updates its content.
40130 refresh : function(){
40131 if(this.refreshDelegate){
40132 this.loaded = false;
40133 this.refreshDelegate();
40138 * Destroys this panel
40140 destroy : function(){
40141 this.el.removeAllListeners();
40142 var tempEl = document.createElement("span");
40143 tempEl.appendChild(this.el.dom);
40144 tempEl.innerHTML = "";
40150 * form - if the content panel contains a form - this is a reference to it.
40151 * @type {Roo.form.Form}
40155 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40156 * This contains a reference to it.
40162 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40172 * @param {Object} cfg Xtype definition of item to add.
40176 getChildContainer: function () {
40177 return this.getEl();
40182 var ret = new Roo.factory(cfg);
40187 if (cfg.xtype.match(/^Form$/)) {
40190 //if (this.footer) {
40191 // el = this.footer.container.insertSibling(false, 'before');
40193 el = this.el.createChild();
40196 this.form = new Roo.form.Form(cfg);
40199 if ( this.form.allItems.length) {
40200 this.form.render(el.dom);
40204 // should only have one of theses..
40205 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40206 // views.. should not be just added - used named prop 'view''
40208 cfg.el = this.el.appendChild(document.createElement("div"));
40211 var ret = new Roo.factory(cfg);
40213 ret.render && ret.render(false, ''); // render blank..
40223 * @class Roo.bootstrap.panel.Grid
40224 * @extends Roo.bootstrap.panel.Content
40226 * Create a new GridPanel.
40227 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40228 * @param {Object} config A the config object
40234 Roo.bootstrap.panel.Grid = function(config)
40238 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40239 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40241 config.el = this.wrapper;
40242 //this.el = this.wrapper;
40244 if (config.container) {
40245 // ctor'ed from a Border/panel.grid
40248 this.wrapper.setStyle("overflow", "hidden");
40249 this.wrapper.addClass('roo-grid-container');
40254 if(config.toolbar){
40255 var tool_el = this.wrapper.createChild();
40256 this.toolbar = Roo.factory(config.toolbar);
40258 if (config.toolbar.items) {
40259 ti = config.toolbar.items ;
40260 delete config.toolbar.items ;
40264 this.toolbar.render(tool_el);
40265 for(var i =0;i < ti.length;i++) {
40266 // Roo.log(['add child', items[i]]);
40267 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40269 this.toolbar.items = nitems;
40271 delete config.toolbar;
40274 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40275 config.grid.scrollBody = true;;
40276 config.grid.monitorWindowResize = false; // turn off autosizing
40277 config.grid.autoHeight = false;
40278 config.grid.autoWidth = false;
40280 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40282 if (config.background) {
40283 // render grid on panel activation (if panel background)
40284 this.on('activate', function(gp) {
40285 if (!gp.grid.rendered) {
40286 gp.grid.render(this.wrapper);
40287 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40292 this.grid.render(this.wrapper);
40293 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40296 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40297 // ??? needed ??? config.el = this.wrapper;
40302 // xtype created footer. - not sure if will work as we normally have to render first..
40303 if (this.footer && !this.footer.el && this.footer.xtype) {
40305 var ctr = this.grid.getView().getFooterPanel(true);
40306 this.footer.dataSource = this.grid.dataSource;
40307 this.footer = Roo.factory(this.footer, Roo);
40308 this.footer.render(ctr);
40318 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40319 getId : function(){
40320 return this.grid.id;
40324 * Returns the grid for this panel
40325 * @return {Roo.bootstrap.Table}
40327 getGrid : function(){
40331 setSize : function(width, height){
40332 if(!this.ignoreResize(width, height)){
40333 var grid = this.grid;
40334 var size = this.adjustForComponents(width, height);
40335 // tfoot is not a footer?
40338 var gridel = grid.getGridEl();
40339 gridel.setSize(size.width, size.height);
40341 var tbd = grid.getGridEl().select('tbody', true).first();
40342 var thd = grid.getGridEl().select('thead',true).first();
40343 var tbf= grid.getGridEl().select('tfoot', true).first();
40346 size.height -= tbf.getHeight();
40349 size.height -= thd.getHeight();
40352 tbd.setSize(size.width, size.height );
40353 // this is for the account management tab -seems to work there.
40354 var thd = grid.getGridEl().select('thead',true).first();
40356 // tbd.setSize(size.width, size.height - thd.getHeight());
40365 beforeSlide : function(){
40366 this.grid.getView().scroller.clip();
40369 afterSlide : function(){
40370 this.grid.getView().scroller.unclip();
40373 destroy : function(){
40374 this.grid.destroy();
40376 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40381 * @class Roo.bootstrap.panel.Nest
40382 * @extends Roo.bootstrap.panel.Content
40384 * Create a new Panel, that can contain a layout.Border.
40387 * @param {Roo.BorderLayout} layout The layout for this panel
40388 * @param {String/Object} config A string to set only the title or a config object
40390 Roo.bootstrap.panel.Nest = function(config)
40392 // construct with only one argument..
40393 /* FIXME - implement nicer consturctors
40394 if (layout.layout) {
40396 layout = config.layout;
40397 delete config.layout;
40399 if (layout.xtype && !layout.getEl) {
40400 // then layout needs constructing..
40401 layout = Roo.factory(layout, Roo);
40405 config.el = config.layout.getEl();
40407 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40409 config.layout.monitorWindowResize = false; // turn off autosizing
40410 this.layout = config.layout;
40411 this.layout.getEl().addClass("roo-layout-nested-layout");
40412 this.layout.parent = this;
40419 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40421 setSize : function(width, height){
40422 if(!this.ignoreResize(width, height)){
40423 var size = this.adjustForComponents(width, height);
40424 var el = this.layout.getEl();
40425 if (size.height < 1) {
40426 el.setWidth(size.width);
40428 el.setSize(size.width, size.height);
40430 var touch = el.dom.offsetWidth;
40431 this.layout.layout();
40432 // ie requires a double layout on the first pass
40433 if(Roo.isIE && !this.initialized){
40434 this.initialized = true;
40435 this.layout.layout();
40440 // activate all subpanels if not currently active..
40442 setActiveState : function(active){
40443 this.active = active;
40444 this.setActiveClass(active);
40447 this.fireEvent("deactivate", this);
40451 this.fireEvent("activate", this);
40452 // not sure if this should happen before or after..
40453 if (!this.layout) {
40454 return; // should not happen..
40457 for (var r in this.layout.regions) {
40458 reg = this.layout.getRegion(r);
40459 if (reg.getActivePanel()) {
40460 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40461 reg.setActivePanel(reg.getActivePanel());
40464 if (!reg.panels.length) {
40467 reg.showPanel(reg.getPanel(0));
40476 * Returns the nested BorderLayout for this panel
40477 * @return {Roo.BorderLayout}
40479 getLayout : function(){
40480 return this.layout;
40484 * Adds a xtype elements to the layout of the nested panel
40488 xtype : 'ContentPanel',
40495 xtype : 'NestedLayoutPanel',
40501 items : [ ... list of content panels or nested layout panels.. ]
40505 * @param {Object} cfg Xtype definition of item to add.
40507 addxtype : function(cfg) {
40508 return this.layout.addxtype(cfg);
40513 * Ext JS Library 1.1.1
40514 * Copyright(c) 2006-2007, Ext JS, LLC.
40516 * Originally Released Under LGPL - original licence link has changed is not relivant.
40519 * <script type="text/javascript">
40522 * @class Roo.TabPanel
40523 * @extends Roo.util.Observable
40524 * A lightweight tab container.
40528 // basic tabs 1, built from existing content
40529 var tabs = new Roo.TabPanel("tabs1");
40530 tabs.addTab("script", "View Script");
40531 tabs.addTab("markup", "View Markup");
40532 tabs.activate("script");
40534 // more advanced tabs, built from javascript
40535 var jtabs = new Roo.TabPanel("jtabs");
40536 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40538 // set up the UpdateManager
40539 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40540 var updater = tab2.getUpdateManager();
40541 updater.setDefaultUrl("ajax1.htm");
40542 tab2.on('activate', updater.refresh, updater, true);
40544 // Use setUrl for Ajax loading
40545 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40546 tab3.setUrl("ajax2.htm", null, true);
40549 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40552 jtabs.activate("jtabs-1");
40555 * Create a new TabPanel.
40556 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40557 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40559 Roo.bootstrap.panel.Tabs = function(config){
40561 * The container element for this TabPanel.
40562 * @type Roo.Element
40564 this.el = Roo.get(config.el);
40567 if(typeof config == "boolean"){
40568 this.tabPosition = config ? "bottom" : "top";
40570 Roo.apply(this, config);
40574 if(this.tabPosition == "bottom"){
40575 // if tabs are at the bottom = create the body first.
40576 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40577 this.el.addClass("roo-tabs-bottom");
40579 // next create the tabs holders
40581 if (this.tabPosition == "west"){
40583 var reg = this.region; // fake it..
40585 if (!reg.mgr.parent) {
40588 reg = reg.mgr.parent.region;
40590 Roo.log("got nest?");
40592 if (reg.mgr.getRegion('west')) {
40593 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40594 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40595 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40596 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40597 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40605 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40606 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40607 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40608 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40613 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40616 // finally - if tabs are at the top, then create the body last..
40617 if(this.tabPosition != "bottom"){
40618 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40619 * @type Roo.Element
40621 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40622 this.el.addClass("roo-tabs-top");
40626 this.bodyEl.setStyle("position", "relative");
40628 this.active = null;
40629 this.activateDelegate = this.activate.createDelegate(this);
40634 * Fires when the active tab changes
40635 * @param {Roo.TabPanel} this
40636 * @param {Roo.TabPanelItem} activePanel The new active tab
40640 * @event beforetabchange
40641 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40642 * @param {Roo.TabPanel} this
40643 * @param {Object} e Set cancel to true on this object to cancel the tab change
40644 * @param {Roo.TabPanelItem} tab The tab being changed to
40646 "beforetabchange" : true
40649 Roo.EventManager.onWindowResize(this.onResize, this);
40650 this.cpad = this.el.getPadding("lr");
40651 this.hiddenCount = 0;
40654 // toolbar on the tabbar support...
40655 if (this.toolbar) {
40656 alert("no toolbar support yet");
40657 this.toolbar = false;
40659 var tcfg = this.toolbar;
40660 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40661 this.toolbar = new Roo.Toolbar(tcfg);
40662 if (Roo.isSafari) {
40663 var tbl = tcfg.container.child('table', true);
40664 tbl.setAttribute('width', '100%');
40672 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40675 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40677 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40679 tabPosition : "top",
40681 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40683 currentTabWidth : 0,
40685 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40689 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40693 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40695 preferredTabWidth : 175,
40697 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40699 resizeTabs : false,
40701 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40703 monitorResize : true,
40705 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40707 toolbar : false, // set by caller..
40709 region : false, /// set by caller
40711 disableTooltips : true, // not used yet...
40714 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40715 * @param {String} id The id of the div to use <b>or create</b>
40716 * @param {String} text The text for the tab
40717 * @param {String} content (optional) Content to put in the TabPanelItem body
40718 * @param {Boolean} closable (optional) True to create a close icon on the tab
40719 * @return {Roo.TabPanelItem} The created TabPanelItem
40721 addTab : function(id, text, content, closable, tpl)
40723 var item = new Roo.bootstrap.panel.TabItem({
40727 closable : closable,
40730 this.addTabItem(item);
40732 item.setContent(content);
40738 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40739 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40740 * @return {Roo.TabPanelItem}
40742 getTab : function(id){
40743 return this.items[id];
40747 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40748 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40750 hideTab : function(id){
40751 var t = this.items[id];
40754 this.hiddenCount++;
40755 this.autoSizeTabs();
40760 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40761 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40763 unhideTab : function(id){
40764 var t = this.items[id];
40766 t.setHidden(false);
40767 this.hiddenCount--;
40768 this.autoSizeTabs();
40773 * Adds an existing {@link Roo.TabPanelItem}.
40774 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40776 addTabItem : function(item)
40778 this.items[item.id] = item;
40779 this.items.push(item);
40780 this.autoSizeTabs();
40781 // if(this.resizeTabs){
40782 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40783 // this.autoSizeTabs();
40785 // item.autoSize();
40790 * Removes a {@link Roo.TabPanelItem}.
40791 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40793 removeTab : function(id){
40794 var items = this.items;
40795 var tab = items[id];
40796 if(!tab) { return; }
40797 var index = items.indexOf(tab);
40798 if(this.active == tab && items.length > 1){
40799 var newTab = this.getNextAvailable(index);
40804 this.stripEl.dom.removeChild(tab.pnode.dom);
40805 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40806 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40808 items.splice(index, 1);
40809 delete this.items[tab.id];
40810 tab.fireEvent("close", tab);
40811 tab.purgeListeners();
40812 this.autoSizeTabs();
40815 getNextAvailable : function(start){
40816 var items = this.items;
40818 // look for a next tab that will slide over to
40819 // replace the one being removed
40820 while(index < items.length){
40821 var item = items[++index];
40822 if(item && !item.isHidden()){
40826 // if one isn't found select the previous tab (on the left)
40829 var item = items[--index];
40830 if(item && !item.isHidden()){
40838 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40839 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40841 disableTab : function(id){
40842 var tab = this.items[id];
40843 if(tab && this.active != tab){
40849 * Enables a {@link Roo.TabPanelItem} that is disabled.
40850 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40852 enableTab : function(id){
40853 var tab = this.items[id];
40858 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40859 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40860 * @return {Roo.TabPanelItem} The TabPanelItem.
40862 activate : function(id)
40864 //Roo.log('activite:' + id);
40866 var tab = this.items[id];
40870 if(tab == this.active || tab.disabled){
40874 this.fireEvent("beforetabchange", this, e, tab);
40875 if(e.cancel !== true && !tab.disabled){
40877 this.active.hide();
40879 this.active = this.items[id];
40880 this.active.show();
40881 this.fireEvent("tabchange", this, this.active);
40887 * Gets the active {@link Roo.TabPanelItem}.
40888 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40890 getActiveTab : function(){
40891 return this.active;
40895 * Updates the tab body element to fit the height of the container element
40896 * for overflow scrolling
40897 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40899 syncHeight : function(targetHeight){
40900 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40901 var bm = this.bodyEl.getMargins();
40902 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40903 this.bodyEl.setHeight(newHeight);
40907 onResize : function(){
40908 if(this.monitorResize){
40909 this.autoSizeTabs();
40914 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40916 beginUpdate : function(){
40917 this.updating = true;
40921 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40923 endUpdate : function(){
40924 this.updating = false;
40925 this.autoSizeTabs();
40929 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40931 autoSizeTabs : function()
40933 var count = this.items.length;
40934 var vcount = count - this.hiddenCount;
40937 this.stripEl.hide();
40939 this.stripEl.show();
40942 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40947 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40948 var availWidth = Math.floor(w / vcount);
40949 var b = this.stripBody;
40950 if(b.getWidth() > w){
40951 var tabs = this.items;
40952 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40953 if(availWidth < this.minTabWidth){
40954 /*if(!this.sleft){ // incomplete scrolling code
40955 this.createScrollButtons();
40958 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40961 if(this.currentTabWidth < this.preferredTabWidth){
40962 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40968 * Returns the number of tabs in this TabPanel.
40971 getCount : function(){
40972 return this.items.length;
40976 * Resizes all the tabs to the passed width
40977 * @param {Number} The new width
40979 setTabWidth : function(width){
40980 this.currentTabWidth = width;
40981 for(var i = 0, len = this.items.length; i < len; i++) {
40982 if(!this.items[i].isHidden()) {
40983 this.items[i].setWidth(width);
40989 * Destroys this TabPanel
40990 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40992 destroy : function(removeEl){
40993 Roo.EventManager.removeResizeListener(this.onResize, this);
40994 for(var i = 0, len = this.items.length; i < len; i++){
40995 this.items[i].purgeListeners();
40997 if(removeEl === true){
40998 this.el.update("");
41003 createStrip : function(container)
41005 var strip = document.createElement("nav");
41006 strip.className = Roo.bootstrap.version == 4 ?
41007 "navbar-light bg-light" :
41008 "navbar navbar-default"; //"x-tabs-wrap";
41009 container.appendChild(strip);
41013 createStripList : function(strip)
41015 // div wrapper for retard IE
41016 // returns the "tr" element.
41017 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41018 //'<div class="x-tabs-strip-wrap">'+
41019 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41020 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41021 return strip.firstChild; //.firstChild.firstChild.firstChild;
41023 createBody : function(container)
41025 var body = document.createElement("div");
41026 Roo.id(body, "tab-body");
41027 //Roo.fly(body).addClass("x-tabs-body");
41028 Roo.fly(body).addClass("tab-content");
41029 container.appendChild(body);
41032 createItemBody :function(bodyEl, id){
41033 var body = Roo.getDom(id);
41035 body = document.createElement("div");
41038 //Roo.fly(body).addClass("x-tabs-item-body");
41039 Roo.fly(body).addClass("tab-pane");
41040 bodyEl.insertBefore(body, bodyEl.firstChild);
41044 createStripElements : function(stripEl, text, closable, tpl)
41046 var td = document.createElement("li"); // was td..
41047 td.className = 'nav-item';
41049 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41052 stripEl.appendChild(td);
41054 td.className = "x-tabs-closable";
41055 if(!this.closeTpl){
41056 this.closeTpl = new Roo.Template(
41057 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41058 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41059 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41062 var el = this.closeTpl.overwrite(td, {"text": text});
41063 var close = el.getElementsByTagName("div")[0];
41064 var inner = el.getElementsByTagName("em")[0];
41065 return {"el": el, "close": close, "inner": inner};
41068 // not sure what this is..
41069 // if(!this.tabTpl){
41070 //this.tabTpl = new Roo.Template(
41071 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41072 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41074 // this.tabTpl = new Roo.Template(
41075 // '<a href="#">' +
41076 // '<span unselectable="on"' +
41077 // (this.disableTooltips ? '' : ' title="{text}"') +
41078 // ' >{text}</span></a>'
41084 var template = tpl || this.tabTpl || false;
41087 template = new Roo.Template(
41088 Roo.bootstrap.version == 4 ?
41090 '<a class="nav-link" href="#" unselectable="on"' +
41091 (this.disableTooltips ? '' : ' title="{text}"') +
41094 '<a class="nav-link" href="#">' +
41095 '<span unselectable="on"' +
41096 (this.disableTooltips ? '' : ' title="{text}"') +
41097 ' >{text}</span></a>'
41102 switch (typeof(template)) {
41106 template = new Roo.Template(template);
41112 var el = template.overwrite(td, {"text": text});
41114 var inner = el.getElementsByTagName("span")[0];
41116 return {"el": el, "inner": inner};
41124 * @class Roo.TabPanelItem
41125 * @extends Roo.util.Observable
41126 * Represents an individual item (tab plus body) in a TabPanel.
41127 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41128 * @param {String} id The id of this TabPanelItem
41129 * @param {String} text The text for the tab of this TabPanelItem
41130 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41132 Roo.bootstrap.panel.TabItem = function(config){
41134 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41135 * @type Roo.TabPanel
41137 this.tabPanel = config.panel;
41139 * The id for this TabPanelItem
41142 this.id = config.id;
41144 this.disabled = false;
41146 this.text = config.text;
41148 this.loaded = false;
41149 this.closable = config.closable;
41152 * The body element for this TabPanelItem.
41153 * @type Roo.Element
41155 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41156 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41157 this.bodyEl.setStyle("display", "block");
41158 this.bodyEl.setStyle("zoom", "1");
41159 //this.hideAction();
41161 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41163 this.el = Roo.get(els.el);
41164 this.inner = Roo.get(els.inner, true);
41165 this.textEl = Roo.bootstrap.version == 4 ?
41166 this.el : Roo.get(this.el.dom.firstChild, true);
41168 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41169 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41172 // this.el.on("mousedown", this.onTabMouseDown, this);
41173 this.el.on("click", this.onTabClick, this);
41175 if(config.closable){
41176 var c = Roo.get(els.close, true);
41177 c.dom.title = this.closeText;
41178 c.addClassOnOver("close-over");
41179 c.on("click", this.closeClick, this);
41185 * Fires when this tab becomes the active tab.
41186 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41187 * @param {Roo.TabPanelItem} this
41191 * @event beforeclose
41192 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41193 * @param {Roo.TabPanelItem} this
41194 * @param {Object} e Set cancel to true on this object to cancel the close.
41196 "beforeclose": true,
41199 * Fires when this tab is closed.
41200 * @param {Roo.TabPanelItem} this
41204 * @event deactivate
41205 * Fires when this tab is no longer the active tab.
41206 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41207 * @param {Roo.TabPanelItem} this
41209 "deactivate" : true
41211 this.hidden = false;
41213 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41216 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41218 purgeListeners : function(){
41219 Roo.util.Observable.prototype.purgeListeners.call(this);
41220 this.el.removeAllListeners();
41223 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41226 this.status_node.addClass("active");
41229 this.tabPanel.stripWrap.repaint();
41231 this.fireEvent("activate", this.tabPanel, this);
41235 * Returns true if this tab is the active tab.
41236 * @return {Boolean}
41238 isActive : function(){
41239 return this.tabPanel.getActiveTab() == this;
41243 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41246 this.status_node.removeClass("active");
41248 this.fireEvent("deactivate", this.tabPanel, this);
41251 hideAction : function(){
41252 this.bodyEl.hide();
41253 this.bodyEl.setStyle("position", "absolute");
41254 this.bodyEl.setLeft("-20000px");
41255 this.bodyEl.setTop("-20000px");
41258 showAction : function(){
41259 this.bodyEl.setStyle("position", "relative");
41260 this.bodyEl.setTop("");
41261 this.bodyEl.setLeft("");
41262 this.bodyEl.show();
41266 * Set the tooltip for the tab.
41267 * @param {String} tooltip The tab's tooltip
41269 setTooltip : function(text){
41270 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41271 this.textEl.dom.qtip = text;
41272 this.textEl.dom.removeAttribute('title');
41274 this.textEl.dom.title = text;
41278 onTabClick : function(e){
41279 e.preventDefault();
41280 this.tabPanel.activate(this.id);
41283 onTabMouseDown : function(e){
41284 e.preventDefault();
41285 this.tabPanel.activate(this.id);
41288 getWidth : function(){
41289 return this.inner.getWidth();
41292 setWidth : function(width){
41293 var iwidth = width - this.linode.getPadding("lr");
41294 this.inner.setWidth(iwidth);
41295 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41296 this.linode.setWidth(width);
41300 * Show or hide the tab
41301 * @param {Boolean} hidden True to hide or false to show.
41303 setHidden : function(hidden){
41304 this.hidden = hidden;
41305 this.linode.setStyle("display", hidden ? "none" : "");
41309 * Returns true if this tab is "hidden"
41310 * @return {Boolean}
41312 isHidden : function(){
41313 return this.hidden;
41317 * Returns the text for this tab
41320 getText : function(){
41324 autoSize : function(){
41325 //this.el.beginMeasure();
41326 this.textEl.setWidth(1);
41328 * #2804 [new] Tabs in Roojs
41329 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41331 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41332 //this.el.endMeasure();
41336 * Sets the text for the tab (Note: this also sets the tooltip text)
41337 * @param {String} text The tab's text and tooltip
41339 setText : function(text){
41341 this.textEl.update(text);
41342 this.setTooltip(text);
41343 //if(!this.tabPanel.resizeTabs){
41344 // this.autoSize();
41348 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41350 activate : function(){
41351 this.tabPanel.activate(this.id);
41355 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41357 disable : function(){
41358 if(this.tabPanel.active != this){
41359 this.disabled = true;
41360 this.status_node.addClass("disabled");
41365 * Enables this TabPanelItem if it was previously disabled.
41367 enable : function(){
41368 this.disabled = false;
41369 this.status_node.removeClass("disabled");
41373 * Sets the content for this TabPanelItem.
41374 * @param {String} content The content
41375 * @param {Boolean} loadScripts true to look for and load scripts
41377 setContent : function(content, loadScripts){
41378 this.bodyEl.update(content, loadScripts);
41382 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41383 * @return {Roo.UpdateManager} The UpdateManager
41385 getUpdateManager : function(){
41386 return this.bodyEl.getUpdateManager();
41390 * Set a URL to be used to load the content for this TabPanelItem.
41391 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41392 * @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)
41393 * @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)
41394 * @return {Roo.UpdateManager} The UpdateManager
41396 setUrl : function(url, params, loadOnce){
41397 if(this.refreshDelegate){
41398 this.un('activate', this.refreshDelegate);
41400 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41401 this.on("activate", this.refreshDelegate);
41402 return this.bodyEl.getUpdateManager();
41406 _handleRefresh : function(url, params, loadOnce){
41407 if(!loadOnce || !this.loaded){
41408 var updater = this.bodyEl.getUpdateManager();
41409 updater.update(url, params, this._setLoaded.createDelegate(this));
41414 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41415 * Will fail silently if the setUrl method has not been called.
41416 * This does not activate the panel, just updates its content.
41418 refresh : function(){
41419 if(this.refreshDelegate){
41420 this.loaded = false;
41421 this.refreshDelegate();
41426 _setLoaded : function(){
41427 this.loaded = true;
41431 closeClick : function(e){
41434 this.fireEvent("beforeclose", this, o);
41435 if(o.cancel !== true){
41436 this.tabPanel.removeTab(this.id);
41440 * The text displayed in the tooltip for the close icon.
41443 closeText : "Close this tab"
41446 * This script refer to:
41447 * Title: International Telephone Input
41448 * Author: Jack O'Connor
41449 * Code version: v12.1.12
41450 * Availability: https://github.com/jackocnr/intl-tel-input.git
41453 Roo.bootstrap.PhoneInputData = function() {
41456 "Afghanistan (افغانستان)",
41461 "Albania (Shqipëri)",
41466 "Algeria (الجزائر)",
41491 "Antigua and Barbuda",
41501 "Armenia (Հայաստան)",
41517 "Austria (Österreich)",
41522 "Azerbaijan (Azərbaycan)",
41532 "Bahrain (البحرين)",
41537 "Bangladesh (বাংলাদেশ)",
41547 "Belarus (Беларусь)",
41552 "Belgium (België)",
41582 "Bosnia and Herzegovina (Босна и Херцеговина)",
41597 "British Indian Ocean Territory",
41602 "British Virgin Islands",
41612 "Bulgaria (България)",
41622 "Burundi (Uburundi)",
41627 "Cambodia (កម្ពុជា)",
41632 "Cameroon (Cameroun)",
41641 ["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"]
41644 "Cape Verde (Kabu Verdi)",
41649 "Caribbean Netherlands",
41660 "Central African Republic (République centrafricaine)",
41680 "Christmas Island",
41686 "Cocos (Keeling) Islands",
41697 "Comoros (جزر القمر)",
41702 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41707 "Congo (Republic) (Congo-Brazzaville)",
41727 "Croatia (Hrvatska)",
41748 "Czech Republic (Česká republika)",
41753 "Denmark (Danmark)",
41768 "Dominican Republic (República Dominicana)",
41772 ["809", "829", "849"]
41790 "Equatorial Guinea (Guinea Ecuatorial)",
41810 "Falkland Islands (Islas Malvinas)",
41815 "Faroe Islands (Føroyar)",
41836 "French Guiana (Guyane française)",
41841 "French Polynesia (Polynésie française)",
41856 "Georgia (საქართველო)",
41861 "Germany (Deutschland)",
41881 "Greenland (Kalaallit Nunaat)",
41918 "Guinea-Bissau (Guiné Bissau)",
41943 "Hungary (Magyarország)",
41948 "Iceland (Ísland)",
41968 "Iraq (العراق)",
41984 "Israel (ישראל)",
42011 "Jordan (الأردن)",
42016 "Kazakhstan (Казахстан)",
42037 "Kuwait (الكويت)",
42042 "Kyrgyzstan (Кыргызстан)",
42052 "Latvia (Latvija)",
42057 "Lebanon (لبنان)",
42072 "Libya (ليبيا)",
42082 "Lithuania (Lietuva)",
42097 "Macedonia (FYROM) (Македонија)",
42102 "Madagascar (Madagasikara)",
42132 "Marshall Islands",
42142 "Mauritania (موريتانيا)",
42147 "Mauritius (Moris)",
42168 "Moldova (Republica Moldova)",
42178 "Mongolia (Монгол)",
42183 "Montenegro (Crna Gora)",
42193 "Morocco (المغرب)",
42199 "Mozambique (Moçambique)",
42204 "Myanmar (Burma) (မြန်မာ)",
42209 "Namibia (Namibië)",
42224 "Netherlands (Nederland)",
42229 "New Caledonia (Nouvelle-Calédonie)",
42264 "North Korea (조선 민주주의 인민 공화국)",
42269 "Northern Mariana Islands",
42285 "Pakistan (پاکستان)",
42295 "Palestine (فلسطين)",
42305 "Papua New Guinea",
42347 "Réunion (La Réunion)",
42353 "Romania (România)",
42369 "Saint Barthélemy",
42380 "Saint Kitts and Nevis",
42390 "Saint Martin (Saint-Martin (partie française))",
42396 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42401 "Saint Vincent and the Grenadines",
42416 "São Tomé and Príncipe (São Tomé e Príncipe)",
42421 "Saudi Arabia (المملكة العربية السعودية)",
42426 "Senegal (Sénégal)",
42456 "Slovakia (Slovensko)",
42461 "Slovenia (Slovenija)",
42471 "Somalia (Soomaaliya)",
42481 "South Korea (대한민국)",
42486 "South Sudan (جنوب السودان)",
42496 "Sri Lanka (ශ්රී ලංකාව)",
42501 "Sudan (السودان)",
42511 "Svalbard and Jan Mayen",
42522 "Sweden (Sverige)",
42527 "Switzerland (Schweiz)",
42532 "Syria (سوريا)",
42577 "Trinidad and Tobago",
42582 "Tunisia (تونس)",
42587 "Turkey (Türkiye)",
42597 "Turks and Caicos Islands",
42607 "U.S. Virgin Islands",
42617 "Ukraine (Україна)",
42622 "United Arab Emirates (الإمارات العربية المتحدة)",
42644 "Uzbekistan (Oʻzbekiston)",
42654 "Vatican City (Città del Vaticano)",
42665 "Vietnam (Việt Nam)",
42670 "Wallis and Futuna (Wallis-et-Futuna)",
42675 "Western Sahara (الصحراء الغربية)",
42681 "Yemen (اليمن)",
42705 * This script refer to:
42706 * Title: International Telephone Input
42707 * Author: Jack O'Connor
42708 * Code version: v12.1.12
42709 * Availability: https://github.com/jackocnr/intl-tel-input.git
42713 * @class Roo.bootstrap.PhoneInput
42714 * @extends Roo.bootstrap.TriggerField
42715 * An input with International dial-code selection
42717 * @cfg {String} defaultDialCode default '+852'
42718 * @cfg {Array} preferedCountries default []
42721 * Create a new PhoneInput.
42722 * @param {Object} config Configuration options
42725 Roo.bootstrap.PhoneInput = function(config) {
42726 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42729 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42731 listWidth: undefined,
42733 selectedClass: 'active',
42735 invalidClass : "has-warning",
42737 validClass: 'has-success',
42739 allowed: '0123456789',
42744 * @cfg {String} defaultDialCode The default dial code when initializing the input
42746 defaultDialCode: '+852',
42749 * @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
42751 preferedCountries: false,
42753 getAutoCreate : function()
42755 var data = Roo.bootstrap.PhoneInputData();
42756 var align = this.labelAlign || this.parentLabelAlign();
42759 this.allCountries = [];
42760 this.dialCodeMapping = [];
42762 for (var i = 0; i < data.length; i++) {
42764 this.allCountries[i] = {
42768 priority: c[3] || 0,
42769 areaCodes: c[4] || null
42771 this.dialCodeMapping[c[2]] = {
42774 priority: c[3] || 0,
42775 areaCodes: c[4] || null
42787 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42788 maxlength: this.max_length,
42789 cls : 'form-control tel-input',
42790 autocomplete: 'new-password'
42793 var hiddenInput = {
42796 cls: 'hidden-tel-input'
42800 hiddenInput.name = this.name;
42803 if (this.disabled) {
42804 input.disabled = true;
42807 var flag_container = {
42824 cls: this.hasFeedback ? 'has-feedback' : '',
42830 cls: 'dial-code-holder',
42837 cls: 'roo-select2-container input-group',
42844 if (this.fieldLabel.length) {
42847 tooltip: 'This field is required'
42853 cls: 'control-label',
42859 html: this.fieldLabel
42862 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42868 if(this.indicatorpos == 'right') {
42869 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42876 if(align == 'left') {
42884 if(this.labelWidth > 12){
42885 label.style = "width: " + this.labelWidth + 'px';
42887 if(this.labelWidth < 13 && this.labelmd == 0){
42888 this.labelmd = this.labelWidth;
42890 if(this.labellg > 0){
42891 label.cls += ' col-lg-' + this.labellg;
42892 input.cls += ' col-lg-' + (12 - this.labellg);
42894 if(this.labelmd > 0){
42895 label.cls += ' col-md-' + this.labelmd;
42896 container.cls += ' col-md-' + (12 - this.labelmd);
42898 if(this.labelsm > 0){
42899 label.cls += ' col-sm-' + this.labelsm;
42900 container.cls += ' col-sm-' + (12 - this.labelsm);
42902 if(this.labelxs > 0){
42903 label.cls += ' col-xs-' + this.labelxs;
42904 container.cls += ' col-xs-' + (12 - this.labelxs);
42914 var settings = this;
42916 ['xs','sm','md','lg'].map(function(size){
42917 if (settings[size]) {
42918 cfg.cls += ' col-' + size + '-' + settings[size];
42922 this.store = new Roo.data.Store({
42923 proxy : new Roo.data.MemoryProxy({}),
42924 reader : new Roo.data.JsonReader({
42935 'name' : 'dialCode',
42939 'name' : 'priority',
42943 'name' : 'areaCodes',
42950 if(!this.preferedCountries) {
42951 this.preferedCountries = [
42958 var p = this.preferedCountries.reverse();
42961 for (var i = 0; i < p.length; i++) {
42962 for (var j = 0; j < this.allCountries.length; j++) {
42963 if(this.allCountries[j].iso2 == p[i]) {
42964 var t = this.allCountries[j];
42965 this.allCountries.splice(j,1);
42966 this.allCountries.unshift(t);
42972 this.store.proxy.data = {
42974 data: this.allCountries
42980 initEvents : function()
42983 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42985 this.indicator = this.indicatorEl();
42986 this.flag = this.flagEl();
42987 this.dialCodeHolder = this.dialCodeHolderEl();
42989 this.trigger = this.el.select('div.flag-box',true).first();
42990 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42995 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42996 _this.list.setWidth(lw);
42999 this.list.on('mouseover', this.onViewOver, this);
43000 this.list.on('mousemove', this.onViewMove, this);
43001 this.inputEl().on("keyup", this.onKeyUp, this);
43002 this.inputEl().on("keypress", this.onKeyPress, this);
43004 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43006 this.view = new Roo.View(this.list, this.tpl, {
43007 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43010 this.view.on('click', this.onViewClick, this);
43011 this.setValue(this.defaultDialCode);
43014 onTriggerClick : function(e)
43016 Roo.log('trigger click');
43021 if(this.isExpanded()){
43023 this.hasFocus = false;
43025 this.store.load({});
43026 this.hasFocus = true;
43031 isExpanded : function()
43033 return this.list.isVisible();
43036 collapse : function()
43038 if(!this.isExpanded()){
43042 Roo.get(document).un('mousedown', this.collapseIf, this);
43043 Roo.get(document).un('mousewheel', this.collapseIf, this);
43044 this.fireEvent('collapse', this);
43048 expand : function()
43052 if(this.isExpanded() || !this.hasFocus){
43056 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43057 this.list.setWidth(lw);
43060 this.restrictHeight();
43062 Roo.get(document).on('mousedown', this.collapseIf, this);
43063 Roo.get(document).on('mousewheel', this.collapseIf, this);
43065 this.fireEvent('expand', this);
43068 restrictHeight : function()
43070 this.list.alignTo(this.inputEl(), this.listAlign);
43071 this.list.alignTo(this.inputEl(), this.listAlign);
43074 onViewOver : function(e, t)
43076 if(this.inKeyMode){
43079 var item = this.view.findItemFromChild(t);
43082 var index = this.view.indexOf(item);
43083 this.select(index, false);
43088 onViewClick : function(view, doFocus, el, e)
43090 var index = this.view.getSelectedIndexes()[0];
43092 var r = this.store.getAt(index);
43095 this.onSelect(r, index);
43097 if(doFocus !== false && !this.blockFocus){
43098 this.inputEl().focus();
43102 onViewMove : function(e, t)
43104 this.inKeyMode = false;
43107 select : function(index, scrollIntoView)
43109 this.selectedIndex = index;
43110 this.view.select(index);
43111 if(scrollIntoView !== false){
43112 var el = this.view.getNode(index);
43114 this.list.scrollChildIntoView(el, false);
43119 createList : function()
43121 this.list = Roo.get(document.body).createChild({
43123 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43124 style: 'display:none'
43127 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43130 collapseIf : function(e)
43132 var in_combo = e.within(this.el);
43133 var in_list = e.within(this.list);
43134 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43136 if (in_combo || in_list || is_list) {
43142 onSelect : function(record, index)
43144 if(this.fireEvent('beforeselect', this, record, index) !== false){
43146 this.setFlagClass(record.data.iso2);
43147 this.setDialCode(record.data.dialCode);
43148 this.hasFocus = false;
43150 this.fireEvent('select', this, record, index);
43154 flagEl : function()
43156 var flag = this.el.select('div.flag',true).first();
43163 dialCodeHolderEl : function()
43165 var d = this.el.select('input.dial-code-holder',true).first();
43172 setDialCode : function(v)
43174 this.dialCodeHolder.dom.value = '+'+v;
43177 setFlagClass : function(n)
43179 this.flag.dom.className = 'flag '+n;
43182 getValue : function()
43184 var v = this.inputEl().getValue();
43185 if(this.dialCodeHolder) {
43186 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43191 setValue : function(v)
43193 var d = this.getDialCode(v);
43195 //invalid dial code
43196 if(v.length == 0 || !d || d.length == 0) {
43198 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43199 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43205 this.setFlagClass(this.dialCodeMapping[d].iso2);
43206 this.setDialCode(d);
43207 this.inputEl().dom.value = v.replace('+'+d,'');
43208 this.hiddenEl().dom.value = this.getValue();
43213 getDialCode : function(v)
43217 if (v.length == 0) {
43218 return this.dialCodeHolder.dom.value;
43222 if (v.charAt(0) != "+") {
43225 var numericChars = "";
43226 for (var i = 1; i < v.length; i++) {
43227 var c = v.charAt(i);
43230 if (this.dialCodeMapping[numericChars]) {
43231 dialCode = v.substr(1, i);
43233 if (numericChars.length == 4) {
43243 this.setValue(this.defaultDialCode);
43247 hiddenEl : function()
43249 return this.el.select('input.hidden-tel-input',true).first();
43252 // after setting val
43253 onKeyUp : function(e){
43254 this.setValue(this.getValue());
43257 onKeyPress : function(e){
43258 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43265 * @class Roo.bootstrap.MoneyField
43266 * @extends Roo.bootstrap.ComboBox
43267 * Bootstrap MoneyField class
43270 * Create a new MoneyField.
43271 * @param {Object} config Configuration options
43274 Roo.bootstrap.MoneyField = function(config) {
43276 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43280 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43283 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43285 allowDecimals : true,
43287 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43289 decimalSeparator : ".",
43291 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43293 decimalPrecision : 0,
43295 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43297 allowNegative : true,
43299 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43303 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43305 minValue : Number.NEGATIVE_INFINITY,
43307 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43309 maxValue : Number.MAX_VALUE,
43311 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43313 minText : "The minimum value for this field is {0}",
43315 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43317 maxText : "The maximum value for this field is {0}",
43319 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43320 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43322 nanText : "{0} is not a valid number",
43324 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43328 * @cfg {String} defaults currency of the MoneyField
43329 * value should be in lkey
43331 defaultCurrency : false,
43333 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43335 thousandsDelimiter : false,
43337 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43348 getAutoCreate : function()
43350 var align = this.labelAlign || this.parentLabelAlign();
43362 cls : 'form-control roo-money-amount-input',
43363 autocomplete: 'new-password'
43366 var hiddenInput = {
43370 cls: 'hidden-number-input'
43373 if(this.max_length) {
43374 input.maxlength = this.max_length;
43378 hiddenInput.name = this.name;
43381 if (this.disabled) {
43382 input.disabled = true;
43385 var clg = 12 - this.inputlg;
43386 var cmd = 12 - this.inputmd;
43387 var csm = 12 - this.inputsm;
43388 var cxs = 12 - this.inputxs;
43392 cls : 'row roo-money-field',
43396 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43400 cls: 'roo-select2-container input-group',
43404 cls : 'form-control roo-money-currency-input',
43405 autocomplete: 'new-password',
43407 name : this.currencyName
43411 cls : 'input-group-addon',
43425 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43429 cls: this.hasFeedback ? 'has-feedback' : '',
43440 if (this.fieldLabel.length) {
43443 tooltip: 'This field is required'
43449 cls: 'control-label',
43455 html: this.fieldLabel
43458 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43464 if(this.indicatorpos == 'right') {
43465 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43472 if(align == 'left') {
43480 if(this.labelWidth > 12){
43481 label.style = "width: " + this.labelWidth + 'px';
43483 if(this.labelWidth < 13 && this.labelmd == 0){
43484 this.labelmd = this.labelWidth;
43486 if(this.labellg > 0){
43487 label.cls += ' col-lg-' + this.labellg;
43488 input.cls += ' col-lg-' + (12 - this.labellg);
43490 if(this.labelmd > 0){
43491 label.cls += ' col-md-' + this.labelmd;
43492 container.cls += ' col-md-' + (12 - this.labelmd);
43494 if(this.labelsm > 0){
43495 label.cls += ' col-sm-' + this.labelsm;
43496 container.cls += ' col-sm-' + (12 - this.labelsm);
43498 if(this.labelxs > 0){
43499 label.cls += ' col-xs-' + this.labelxs;
43500 container.cls += ' col-xs-' + (12 - this.labelxs);
43511 var settings = this;
43513 ['xs','sm','md','lg'].map(function(size){
43514 if (settings[size]) {
43515 cfg.cls += ' col-' + size + '-' + settings[size];
43522 initEvents : function()
43524 this.indicator = this.indicatorEl();
43526 this.initCurrencyEvent();
43528 this.initNumberEvent();
43531 initCurrencyEvent : function()
43534 throw "can not find store for combo";
43537 this.store = Roo.factory(this.store, Roo.data);
43538 this.store.parent = this;
43542 this.triggerEl = this.el.select('.input-group-addon', true).first();
43544 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43549 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43550 _this.list.setWidth(lw);
43553 this.list.on('mouseover', this.onViewOver, this);
43554 this.list.on('mousemove', this.onViewMove, this);
43555 this.list.on('scroll', this.onViewScroll, this);
43558 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43561 this.view = new Roo.View(this.list, this.tpl, {
43562 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43565 this.view.on('click', this.onViewClick, this);
43567 this.store.on('beforeload', this.onBeforeLoad, this);
43568 this.store.on('load', this.onLoad, this);
43569 this.store.on('loadexception', this.onLoadException, this);
43571 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43572 "up" : function(e){
43573 this.inKeyMode = true;
43577 "down" : function(e){
43578 if(!this.isExpanded()){
43579 this.onTriggerClick();
43581 this.inKeyMode = true;
43586 "enter" : function(e){
43589 if(this.fireEvent("specialkey", this, e)){
43590 this.onViewClick(false);
43596 "esc" : function(e){
43600 "tab" : function(e){
43603 if(this.fireEvent("specialkey", this, e)){
43604 this.onViewClick(false);
43612 doRelay : function(foo, bar, hname){
43613 if(hname == 'down' || this.scope.isExpanded()){
43614 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43622 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43626 initNumberEvent : function(e)
43628 this.inputEl().on("keydown" , this.fireKey, this);
43629 this.inputEl().on("focus", this.onFocus, this);
43630 this.inputEl().on("blur", this.onBlur, this);
43632 this.inputEl().relayEvent('keyup', this);
43634 if(this.indicator){
43635 this.indicator.addClass('invisible');
43638 this.originalValue = this.getValue();
43640 if(this.validationEvent == 'keyup'){
43641 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43642 this.inputEl().on('keyup', this.filterValidation, this);
43644 else if(this.validationEvent !== false){
43645 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43648 if(this.selectOnFocus){
43649 this.on("focus", this.preFocus, this);
43652 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43653 this.inputEl().on("keypress", this.filterKeys, this);
43655 this.inputEl().relayEvent('keypress', this);
43658 var allowed = "0123456789";
43660 if(this.allowDecimals){
43661 allowed += this.decimalSeparator;
43664 if(this.allowNegative){
43668 if(this.thousandsDelimiter) {
43672 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43674 var keyPress = function(e){
43676 var k = e.getKey();
43678 var c = e.getCharCode();
43681 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43682 allowed.indexOf(String.fromCharCode(c)) === -1
43688 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43692 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43697 this.inputEl().on("keypress", keyPress, this);
43701 onTriggerClick : function(e)
43708 this.loadNext = false;
43710 if(this.isExpanded()){
43715 this.hasFocus = true;
43717 if(this.triggerAction == 'all') {
43718 this.doQuery(this.allQuery, true);
43722 this.doQuery(this.getRawValue());
43725 getCurrency : function()
43727 var v = this.currencyEl().getValue();
43732 restrictHeight : function()
43734 this.list.alignTo(this.currencyEl(), this.listAlign);
43735 this.list.alignTo(this.currencyEl(), this.listAlign);
43738 onViewClick : function(view, doFocus, el, e)
43740 var index = this.view.getSelectedIndexes()[0];
43742 var r = this.store.getAt(index);
43745 this.onSelect(r, index);
43749 onSelect : function(record, index){
43751 if(this.fireEvent('beforeselect', this, record, index) !== false){
43753 this.setFromCurrencyData(index > -1 ? record.data : false);
43757 this.fireEvent('select', this, record, index);
43761 setFromCurrencyData : function(o)
43765 this.lastCurrency = o;
43767 if (this.currencyField) {
43768 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43770 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43773 this.lastSelectionText = currency;
43775 //setting default currency
43776 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43777 this.setCurrency(this.defaultCurrency);
43781 this.setCurrency(currency);
43784 setFromData : function(o)
43788 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43790 this.setFromCurrencyData(c);
43795 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43797 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43800 this.setValue(value);
43804 setCurrency : function(v)
43806 this.currencyValue = v;
43809 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43814 setValue : function(v)
43816 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43822 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43824 this.inputEl().dom.value = (v == '') ? '' :
43825 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43827 if(!this.allowZero && v === '0') {
43828 this.hiddenEl().dom.value = '';
43829 this.inputEl().dom.value = '';
43836 getRawValue : function()
43838 var v = this.inputEl().getValue();
43843 getValue : function()
43845 return this.fixPrecision(this.parseValue(this.getRawValue()));
43848 parseValue : function(value)
43850 if(this.thousandsDelimiter) {
43852 r = new RegExp(",", "g");
43853 value = value.replace(r, "");
43856 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43857 return isNaN(value) ? '' : value;
43861 fixPrecision : function(value)
43863 if(this.thousandsDelimiter) {
43865 r = new RegExp(",", "g");
43866 value = value.replace(r, "");
43869 var nan = isNaN(value);
43871 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43872 return nan ? '' : value;
43874 return parseFloat(value).toFixed(this.decimalPrecision);
43877 decimalPrecisionFcn : function(v)
43879 return Math.floor(v);
43882 validateValue : function(value)
43884 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43888 var num = this.parseValue(value);
43891 this.markInvalid(String.format(this.nanText, value));
43895 if(num < this.minValue){
43896 this.markInvalid(String.format(this.minText, this.minValue));
43900 if(num > this.maxValue){
43901 this.markInvalid(String.format(this.maxText, this.maxValue));
43908 validate : function()
43910 if(this.disabled || this.allowBlank){
43915 var currency = this.getCurrency();
43917 if(this.validateValue(this.getRawValue()) && currency.length){
43922 this.markInvalid();
43926 getName: function()
43931 beforeBlur : function()
43937 var v = this.parseValue(this.getRawValue());
43944 onBlur : function()
43948 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43949 //this.el.removeClass(this.focusClass);
43952 this.hasFocus = false;
43954 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43958 var v = this.getValue();
43960 if(String(v) !== String(this.startValue)){
43961 this.fireEvent('change', this, v, this.startValue);
43964 this.fireEvent("blur", this);
43967 inputEl : function()
43969 return this.el.select('.roo-money-amount-input', true).first();
43972 currencyEl : function()
43974 return this.el.select('.roo-money-currency-input', true).first();
43977 hiddenEl : function()
43979 return this.el.select('input.hidden-number-input',true).first();
43983 * @class Roo.bootstrap.BezierSignature
43984 * @extends Roo.bootstrap.Component
43985 * Bootstrap BezierSignature class
43986 * This script refer to:
43987 * Title: Signature Pad
43989 * Availability: https://github.com/szimek/signature_pad
43992 * Create a new BezierSignature
43993 * @param {Object} config The config object
43996 Roo.bootstrap.BezierSignature = function(config){
43997 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44003 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44010 mouse_btn_down: true,
44013 * @cfg {int} canvas height
44015 canvas_height: '200px',
44018 * @cfg {float|function} Radius of a single dot.
44023 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44028 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44033 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44038 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44043 * @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.
44045 bg_color: 'rgba(0, 0, 0, 0)',
44048 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44050 dot_color: 'black',
44053 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44055 velocity_filter_weight: 0.7,
44058 * @cfg {function} Callback when stroke begin.
44063 * @cfg {function} Callback when stroke end.
44067 getAutoCreate : function()
44069 var cls = 'roo-signature column';
44072 cls += ' ' + this.cls;
44082 for(var i = 0; i < col_sizes.length; i++) {
44083 if(this[col_sizes[i]]) {
44084 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44094 cls: 'roo-signature-body',
44098 cls: 'roo-signature-body-canvas',
44099 height: this.canvas_height,
44100 width: this.canvas_width
44107 style: 'display: none'
44115 initEvents: function()
44117 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44119 var canvas = this.canvasEl();
44121 // mouse && touch event swapping...
44122 canvas.dom.style.touchAction = 'none';
44123 canvas.dom.style.msTouchAction = 'none';
44125 this.mouse_btn_down = false;
44126 canvas.on('mousedown', this._handleMouseDown, this);
44127 canvas.on('mousemove', this._handleMouseMove, this);
44128 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44130 if (window.PointerEvent) {
44131 canvas.on('pointerdown', this._handleMouseDown, this);
44132 canvas.on('pointermove', this._handleMouseMove, this);
44133 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44136 if ('ontouchstart' in window) {
44137 canvas.on('touchstart', this._handleTouchStart, this);
44138 canvas.on('touchmove', this._handleTouchMove, this);
44139 canvas.on('touchend', this._handleTouchEnd, this);
44142 Roo.EventManager.onWindowResize(this.resize, this, true);
44144 // file input event
44145 this.fileEl().on('change', this.uploadImage, this);
44152 resize: function(){
44154 var canvas = this.canvasEl().dom;
44155 var ctx = this.canvasElCtx();
44156 var img_data = false;
44158 if(canvas.width > 0) {
44159 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44161 // setting canvas width will clean img data
44164 var style = window.getComputedStyle ?
44165 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44167 var padding_left = parseInt(style.paddingLeft) || 0;
44168 var padding_right = parseInt(style.paddingRight) || 0;
44170 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44173 ctx.putImageData(img_data, 0, 0);
44177 _handleMouseDown: function(e)
44179 if (e.browserEvent.which === 1) {
44180 this.mouse_btn_down = true;
44181 this.strokeBegin(e);
44185 _handleMouseMove: function (e)
44187 if (this.mouse_btn_down) {
44188 this.strokeMoveUpdate(e);
44192 _handleMouseUp: function (e)
44194 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44195 this.mouse_btn_down = false;
44200 _handleTouchStart: function (e) {
44202 e.preventDefault();
44203 if (e.browserEvent.targetTouches.length === 1) {
44204 // var touch = e.browserEvent.changedTouches[0];
44205 // this.strokeBegin(touch);
44207 this.strokeBegin(e); // assume e catching the correct xy...
44211 _handleTouchMove: function (e) {
44212 e.preventDefault();
44213 // var touch = event.targetTouches[0];
44214 // _this._strokeMoveUpdate(touch);
44215 this.strokeMoveUpdate(e);
44218 _handleTouchEnd: function (e) {
44219 var wasCanvasTouched = e.target === this.canvasEl().dom;
44220 if (wasCanvasTouched) {
44221 e.preventDefault();
44222 // var touch = event.changedTouches[0];
44223 // _this._strokeEnd(touch);
44228 reset: function () {
44229 this._lastPoints = [];
44230 this._lastVelocity = 0;
44231 this._lastWidth = (this.min_width + this.max_width) / 2;
44232 this.canvasElCtx().fillStyle = this.dot_color;
44235 strokeMoveUpdate: function(e)
44237 this.strokeUpdate(e);
44239 if (this.throttle) {
44240 this.throttleStroke(this.strokeUpdate, this.throttle);
44243 this.strokeUpdate(e);
44247 strokeBegin: function(e)
44249 var newPointGroup = {
44250 color: this.dot_color,
44254 if (typeof this.onBegin === 'function') {
44258 this.curve_data.push(newPointGroup);
44260 this.strokeUpdate(e);
44263 strokeUpdate: function(e)
44265 var rect = this.canvasEl().dom.getBoundingClientRect();
44266 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44267 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44268 var lastPoints = lastPointGroup.points;
44269 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44270 var isLastPointTooClose = lastPoint
44271 ? point.distanceTo(lastPoint) <= this.min_distance
44273 var color = lastPointGroup.color;
44274 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44275 var curve = this.addPoint(point);
44277 this.drawDot({color: color, point: point});
44280 this.drawCurve({color: color, curve: curve});
44290 strokeEnd: function(e)
44292 this.strokeUpdate(e);
44293 if (typeof this.onEnd === 'function') {
44298 addPoint: function (point) {
44299 var _lastPoints = this._lastPoints;
44300 _lastPoints.push(point);
44301 if (_lastPoints.length > 2) {
44302 if (_lastPoints.length === 3) {
44303 _lastPoints.unshift(_lastPoints[0]);
44305 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44306 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44307 _lastPoints.shift();
44313 calculateCurveWidths: function (startPoint, endPoint) {
44314 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44315 (1 - this.velocity_filter_weight) * this._lastVelocity;
44317 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44320 start: this._lastWidth
44323 this._lastVelocity = velocity;
44324 this._lastWidth = newWidth;
44328 drawDot: function (_a) {
44329 var color = _a.color, point = _a.point;
44330 var ctx = this.canvasElCtx();
44331 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44333 this.drawCurveSegment(point.x, point.y, width);
44335 ctx.fillStyle = color;
44339 drawCurve: function (_a) {
44340 var color = _a.color, curve = _a.curve;
44341 var ctx = this.canvasElCtx();
44342 var widthDelta = curve.endWidth - curve.startWidth;
44343 var drawSteps = Math.floor(curve.length()) * 2;
44345 ctx.fillStyle = color;
44346 for (var i = 0; i < drawSteps; i += 1) {
44347 var t = i / drawSteps;
44353 var x = uuu * curve.startPoint.x;
44354 x += 3 * uu * t * curve.control1.x;
44355 x += 3 * u * tt * curve.control2.x;
44356 x += ttt * curve.endPoint.x;
44357 var y = uuu * curve.startPoint.y;
44358 y += 3 * uu * t * curve.control1.y;
44359 y += 3 * u * tt * curve.control2.y;
44360 y += ttt * curve.endPoint.y;
44361 var width = curve.startWidth + ttt * widthDelta;
44362 this.drawCurveSegment(x, y, width);
44368 drawCurveSegment: function (x, y, width) {
44369 var ctx = this.canvasElCtx();
44371 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44372 this.is_empty = false;
44377 var ctx = this.canvasElCtx();
44378 var canvas = this.canvasEl().dom;
44379 ctx.fillStyle = this.bg_color;
44380 ctx.clearRect(0, 0, canvas.width, canvas.height);
44381 ctx.fillRect(0, 0, canvas.width, canvas.height);
44382 this.curve_data = [];
44384 this.is_empty = true;
44389 return this.el.select('input',true).first();
44392 canvasEl: function()
44394 return this.el.select('canvas',true).first();
44397 canvasElCtx: function()
44399 return this.el.select('canvas',true).first().dom.getContext('2d');
44402 getImage: function(type)
44404 if(this.is_empty) {
44409 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44412 drawFromImage: function(img_src)
44414 var img = new Image();
44416 img.onload = function(){
44417 this.canvasElCtx().drawImage(img, 0, 0);
44422 this.is_empty = false;
44425 selectImage: function()
44427 this.fileEl().dom.click();
44430 uploadImage: function(e)
44432 var reader = new FileReader();
44434 reader.onload = function(e){
44435 var img = new Image();
44436 img.onload = function(){
44438 this.canvasElCtx().drawImage(img, 0, 0);
44440 img.src = e.target.result;
44443 reader.readAsDataURL(e.target.files[0]);
44446 // Bezier Point Constructor
44447 Point: (function () {
44448 function Point(x, y, time) {
44451 this.time = time || Date.now();
44453 Point.prototype.distanceTo = function (start) {
44454 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44456 Point.prototype.equals = function (other) {
44457 return this.x === other.x && this.y === other.y && this.time === other.time;
44459 Point.prototype.velocityFrom = function (start) {
44460 return this.time !== start.time
44461 ? this.distanceTo(start) / (this.time - start.time)
44468 // Bezier Constructor
44469 Bezier: (function () {
44470 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44471 this.startPoint = startPoint;
44472 this.control2 = control2;
44473 this.control1 = control1;
44474 this.endPoint = endPoint;
44475 this.startWidth = startWidth;
44476 this.endWidth = endWidth;
44478 Bezier.fromPoints = function (points, widths, scope) {
44479 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44480 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44481 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44483 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44484 var dx1 = s1.x - s2.x;
44485 var dy1 = s1.y - s2.y;
44486 var dx2 = s2.x - s3.x;
44487 var dy2 = s2.y - s3.y;
44488 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44489 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44490 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44491 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44492 var dxm = m1.x - m2.x;
44493 var dym = m1.y - m2.y;
44494 var k = l2 / (l1 + l2);
44495 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44496 var tx = s2.x - cm.x;
44497 var ty = s2.y - cm.y;
44499 c1: new scope.Point(m1.x + tx, m1.y + ty),
44500 c2: new scope.Point(m2.x + tx, m2.y + ty)
44503 Bezier.prototype.length = function () {
44508 for (var i = 0; i <= steps; i += 1) {
44510 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44511 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44513 var xdiff = cx - px;
44514 var ydiff = cy - py;
44515 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44522 Bezier.prototype.point = function (t, start, c1, c2, end) {
44523 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44524 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44525 + (3.0 * c2 * (1.0 - t) * t * t)
44526 + (end * t * t * t);
44531 throttleStroke: function(fn, wait) {
44532 if (wait === void 0) { wait = 250; }
44534 var timeout = null;
44538 var later = function () {
44539 previous = Date.now();
44541 result = fn.apply(storedContext, storedArgs);
44543 storedContext = null;
44547 return function wrapper() {
44549 for (var _i = 0; _i < arguments.length; _i++) {
44550 args[_i] = arguments[_i];
44552 var now = Date.now();
44553 var remaining = wait - (now - previous);
44554 storedContext = this;
44556 if (remaining <= 0 || remaining > wait) {
44558 clearTimeout(timeout);
44562 result = fn.apply(storedContext, storedArgs);
44564 storedContext = null;
44568 else if (!timeout) {
44569 timeout = window.setTimeout(later, remaining);