2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
679 preventDefault: false,
682 getAutoCreate : function(){
686 // cls: this.cls, double assign in parent class Component.js :: onRender
693 initEvents: function()
695 Roo.bootstrap.Element.superclass.initEvents.call(this);
698 this.el.on('click', this.onClick, this);
703 onClick : function(e)
705 if(this.preventDefault){
709 this.fireEvent('click', this, e);
712 getValue : function()
714 return this.el.dom.innerHTML;
717 setValue : function(value)
719 this.el.dom.innerHTML = value;
734 * @class Roo.bootstrap.DropTarget
735 * @extends Roo.bootstrap.Element
736 * Bootstrap DropTarget class
738 * @cfg {string} name dropable name
741 * Create a new Dropable Area
742 * @param {Object} config The config object
745 Roo.bootstrap.DropTarget = function(config){
746 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
752 * When a element is chick
753 * @param {Roo.bootstrap.Element} this
754 * @param {Roo.EventObject} e
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
763 getAutoCreate : function(){
768 initEvents: function()
770 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
774 drop : this.dragDrop.createDelegate(this),
775 enter : this.dragEnter.createDelegate(this),
776 out : this.dragOut.createDelegate(this),
777 over : this.dragOver.createDelegate(this)
781 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
784 dragDrop : function(source,e,data)
786 // user has to decide how to impliment this.
789 //this.fireEvent('drop', this, source, e ,data);
793 dragEnter : function(n, dd, e, data)
795 // probably want to resize the element to match the dropped element..
797 this.originalSize = this.el.getSize();
798 this.el.setSize( n.el.getSize());
799 this.dropZone.DDM.refreshCache(this.name);
800 Roo.log([n, dd, e, data]);
803 dragOut : function(value)
805 // resize back to normal
807 this.el.setSize(this.originalSize);
808 this.dropZone.resetConstraints();
811 dragOver : function()
828 * @class Roo.bootstrap.Body
829 * @extends Roo.bootstrap.Component
830 * Bootstrap Body class
834 * @param {Object} config The config object
837 Roo.bootstrap.Body = function(config){
839 config = config || {};
841 Roo.bootstrap.Body.superclass.constructor.call(this, config);
842 this.el = Roo.get(config.el ? config.el : document.body );
843 if (this.cls && this.cls.length) {
844 Roo.get(document.body).addClass(this.cls);
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
850 is_body : true,// just to make sure it's constructed?
855 onRender : function(ct, position)
857 /* Roo.log("Roo.bootstrap.Body - onRender");
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
878 * @class Roo.bootstrap.ButtonGroup
879 * @extends Roo.bootstrap.Component
880 * Bootstrap ButtonGroup class
881 * @cfg {String} size lg | sm | xs (default empty normal)
882 * @cfg {String} align vertical | justified (default none)
883 * @cfg {String} direction up | down (default down)
884 * @cfg {Boolean} toolbar false | true
885 * @cfg {Boolean} btn true | false
890 * @param {Object} config The config object
893 Roo.bootstrap.ButtonGroup = function(config){
894 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
905 getAutoCreate : function(){
911 cfg.html = this.html || cfg.html;
922 if (['vertical','justified'].indexOf(this.align)!==-1) {
923 cfg.cls = 'btn-group-' + this.align;
925 if (this.align == 'justified') {
926 console.log(this.items);
930 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931 cfg.cls += ' btn-group-' + this.size;
934 if (this.direction == 'up') {
935 cfg.cls += ' dropup' ;
941 * Add a button to the group (similar to NavItem API.)
943 addItem : function(cfg)
945 var cn = new Roo.bootstrap.Button(cfg);
947 cn.parentId = this.id;
948 cn.onRender(this.el, null);
962 * @class Roo.bootstrap.Button
963 * @extends Roo.bootstrap.Component
964 * Bootstrap Button class
965 * @cfg {String} html The button content
966 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969 * @cfg {String} size (lg|sm|xs)
970 * @cfg {String} tag (a|input|submit)
971 * @cfg {String} href empty or href
972 * @cfg {Boolean} disabled default false;
973 * @cfg {Boolean} isClose default false;
974 * @cfg {String} glyphicon depricated - use fa
975 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976 * @cfg {String} badge text for badge
977 * @cfg {String} theme (default|glow)
978 * @cfg {Boolean} inverse dark themed version
979 * @cfg {Boolean} toggle is it a slidy toggle button
980 * @cfg {Boolean} pressed default null - if the button ahs active state
981 * @cfg {String} ontext text for on slidy toggle state
982 * @cfg {String} offtext text for off slidy toggle state
983 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
984 * @cfg {Boolean} removeClass remove the standard class..
985 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
986 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
989 * Create a new button
990 * @param {Object} config The config object
994 Roo.bootstrap.Button = function(config){
995 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1001 * When a button is pressed
1002 * @param {Roo.bootstrap.Button} btn
1003 * @param {Roo.EventObject} e
1008 * When a button is double clicked
1009 * @param {Roo.bootstrap.Button} btn
1010 * @param {Roo.EventObject} e
1015 * After the button has been toggles
1016 * @param {Roo.bootstrap.Button} btn
1017 * @param {Roo.EventObject} e
1018 * @param {boolean} pressed (also available as button.pressed)
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1045 preventDefault: true,
1054 getAutoCreate : function(){
1062 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064 this.tag = 'button';
1068 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1070 if (this.toggle == true) {
1073 cls: 'slider-frame roo-button',
1077 'data-on-text':'ON',
1078 'data-off-text':'OFF',
1079 cls: 'slider-button',
1084 // why are we validating the weights?
1085 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086 cfg.cls += ' ' + this.weight;
1093 cfg.cls += ' close';
1095 cfg["aria-hidden"] = true;
1097 cfg.html = "×";
1103 if (this.theme==='default') {
1104 cfg.cls = 'btn roo-button';
1106 //if (this.parentType != 'Navbar') {
1107 this.weight = this.weight.length ? this.weight : 'default';
1109 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1111 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113 cfg.cls += ' btn-' + outline + weight;
1114 if (this.weight == 'default') {
1116 cfg.cls += ' btn-' + this.weight;
1119 } else if (this.theme==='glow') {
1122 cfg.cls = 'btn-glow roo-button';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 cfg.cls += ' ' + this.weight;
1132 this.cls += ' inverse';
1136 if (this.active || this.pressed === true) {
1137 cfg.cls += ' active';
1140 if (this.disabled) {
1141 cfg.disabled = 'disabled';
1145 Roo.log('changing to ul' );
1147 this.glyphicon = 'caret';
1148 if (Roo.bootstrap.version == 4) {
1149 this.fa = 'caret-down';
1154 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1156 //gsRoo.log(this.parentType);
1157 if (this.parentType === 'Navbar' && !this.parent().bar) {
1158 Roo.log('changing to li?');
1167 href : this.href || '#'
1170 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1171 cfg.cls += ' dropdown';
1178 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1180 if (this.glyphicon) {
1181 cfg.html = ' ' + cfg.html;
1186 cls: 'glyphicon glyphicon-' + this.glyphicon
1191 cfg.html = ' ' + cfg.html;
1196 cls: 'fa fas fa-' + this.fa
1206 // cfg.cls='btn roo-button';
1210 var value = cfg.html;
1215 cls: 'glyphicon glyphicon-' + this.glyphicon,
1222 cls: 'fa fas fa-' + this.fa,
1227 var bw = this.badge_weight.length ? this.badge_weight :
1228 (this.weight.length ? this.weight : 'secondary');
1229 bw = bw == 'default' ? 'secondary' : bw;
1235 cls: 'badge badge-' + bw,
1244 cfg.cls += ' dropdown';
1245 cfg.html = typeof(cfg.html) != 'undefined' ?
1246 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1249 if (cfg.tag !== 'a' && this.href !== '') {
1250 throw "Tag must be a to set href.";
1251 } else if (this.href.length > 0) {
1252 cfg.href = this.href;
1255 if(this.removeClass){
1260 cfg.target = this.target;
1265 initEvents: function() {
1266 // Roo.log('init events?');
1267 // Roo.log(this.el.dom);
1270 if (typeof (this.menu) != 'undefined') {
1271 this.menu.parentType = this.xtype;
1272 this.menu.triggerEl = this.el;
1273 this.addxtype(Roo.apply({}, this.menu));
1277 if (this.el.hasClass('roo-button')) {
1278 this.el.on('click', this.onClick, this);
1279 this.el.on('dblclick', this.onDblClick, this);
1281 this.el.select('.roo-button').on('click', this.onClick, this);
1282 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1286 if(this.removeClass){
1287 this.el.on('click', this.onClick, this);
1290 if (this.group === true) {
1291 if (this.pressed === false || this.pressed === true) {
1294 this.pressed = false;
1295 this.setActive(this.pressed);
1300 this.el.enableDisplayMode();
1303 onClick : function(e)
1305 if (this.disabled) {
1309 Roo.log('button on click ');
1310 if(this.preventDefault){
1319 this.setActive(true);
1320 var pi = this.parent().items;
1321 for (var i = 0;i < pi.length;i++) {
1322 if (this == pi[i]) {
1325 if (pi[i].el.hasClass('roo-button')) {
1326 pi[i].setActive(false);
1329 this.fireEvent('click', this, e);
1333 if (this.pressed === true || this.pressed === false) {
1334 this.toggleActive(e);
1338 this.fireEvent('click', this, e);
1340 onDblClick: function(e)
1342 if (this.disabled) {
1345 if(this.preventDefault){
1348 this.fireEvent('dblclick', this, e);
1351 * Enables this button
1355 this.disabled = false;
1356 this.el.removeClass('disabled');
1360 * Disable this button
1362 disable : function()
1364 this.disabled = true;
1365 this.el.addClass('disabled');
1368 * sets the active state on/off,
1369 * @param {Boolean} state (optional) Force a particular state
1371 setActive : function(v) {
1373 this.el[v ? 'addClass' : 'removeClass']('active');
1377 * toggles the current active state
1379 toggleActive : function(e)
1381 this.setActive(!this.pressed); // this modifies pressed...
1382 this.fireEvent('toggle', this, e, this.pressed);
1385 * get the current active state
1386 * @return {boolean} true if it's active
1388 isActive : function()
1390 return this.el.hasClass('active');
1393 * set the text of the first selected button
1395 setText : function(str)
1397 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400 * get the text of the first selected button
1402 getText : function()
1404 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407 setWeight : function(str)
1409 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1412 var outline = this.outline ? 'outline-' : '';
1413 if (str == 'default') {
1414 this.el.addClass('btn-default btn-outline-secondary');
1417 this.el.addClass('btn-' + outline + str);
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1424 Roo.bootstrap.Button.weights = [
1444 * @class Roo.bootstrap.Column
1445 * @extends Roo.bootstrap.Component
1446 * Bootstrap Column class
1447 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457 * @cfg {Boolean} hidden (true|false) hide the element
1458 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459 * @cfg {String} fa (ban|check|...) font awesome icon
1460 * @cfg {Number} fasize (1|2|....) font awsome size
1462 * @cfg {String} icon (info-sign|check|...) glyphicon name
1464 * @cfg {String} html content of column.
1467 * Create a new Column
1468 * @param {Object} config The config object
1471 Roo.bootstrap.Column = function(config){
1472 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1493 getAutoCreate : function(){
1494 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1502 var sizes = ['xs','sm','md','lg'];
1503 sizes.map(function(size ,ix){
1504 //Roo.log( size + ':' + settings[size]);
1506 if (settings[size+'off'] !== false) {
1507 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510 if (settings[size] === false) {
1514 if (!settings[size]) { // 0 = hidden
1515 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1517 for (var i = ix; i > -1; i--) {
1518 cfg.cls += ' d-' + sizes[i] + '-none';
1524 cfg.cls += ' col-' + size + '-' + settings[size] + (
1525 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1531 cfg.cls += ' hidden';
1534 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535 cfg.cls +=' alert alert-' + this.alert;
1539 if (this.html.length) {
1540 cfg.html = this.html;
1544 if (this.fasize > 1) {
1545 fasize = ' fa-' + this.fasize + 'x';
1547 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1552 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1571 * @class Roo.bootstrap.Container
1572 * @extends Roo.bootstrap.Component
1573 * Bootstrap Container class
1574 * @cfg {Boolean} jumbotron is it a jumbotron element
1575 * @cfg {String} html content of element
1576 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1578 * @cfg {String} header content of header (for panel)
1579 * @cfg {String} footer content of footer (for panel)
1580 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581 * @cfg {String} tag (header|aside|section) type of HTML tag.
1582 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583 * @cfg {String} fa font awesome icon
1584 * @cfg {String} icon (info-sign|check|...) glyphicon name
1585 * @cfg {Boolean} hidden (true|false) hide the element
1586 * @cfg {Boolean} expandable (true|false) default false
1587 * @cfg {Boolean} expanded (true|false) default true
1588 * @cfg {String} rheader contet on the right of header
1589 * @cfg {Boolean} clickable (true|false) default false
1593 * Create a new Container
1594 * @param {Object} config The config object
1597 Roo.bootstrap.Container = function(config){
1598 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1604 * After the panel has been expand
1606 * @param {Roo.bootstrap.Container} this
1611 * After the panel has been collapsed
1613 * @param {Roo.bootstrap.Container} this
1618 * When a element is chick
1619 * @param {Roo.bootstrap.Container} this
1620 * @param {Roo.EventObject} e
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1644 getChildContainer : function() {
1650 if (this.panel.length) {
1651 return this.el.select('.panel-body',true).first();
1658 getAutoCreate : function(){
1661 tag : this.tag || 'div',
1665 if (this.jumbotron) {
1666 cfg.cls = 'jumbotron';
1671 // - this is applied by the parent..
1673 // cfg.cls = this.cls + '';
1676 if (this.sticky.length) {
1678 var bd = Roo.get(document.body);
1679 if (!bd.hasClass('bootstrap-sticky')) {
1680 bd.addClass('bootstrap-sticky');
1681 Roo.select('html',true).setStyle('height', '100%');
1684 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1688 if (this.well.length) {
1689 switch (this.well) {
1692 cfg.cls +=' well well-' +this.well;
1701 cfg.cls += ' hidden';
1705 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706 cfg.cls +=' alert alert-' + this.alert;
1711 if (this.panel.length) {
1712 cfg.cls += ' panel panel-' + this.panel;
1714 if (this.header.length) {
1718 if(this.expandable){
1720 cfg.cls = cfg.cls + ' expandable';
1724 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1732 cls : 'panel-title',
1733 html : (this.expandable ? ' ' : '') + this.header
1737 cls: 'panel-header-right',
1743 cls : 'panel-heading',
1744 style : this.expandable ? 'cursor: pointer' : '',
1752 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1757 if (this.footer.length) {
1759 cls : 'panel-footer',
1768 body.html = this.html || cfg.html;
1769 // prefix with the icons..
1771 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1779 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780 cfg.cls = 'container';
1786 initEvents: function()
1788 if(this.expandable){
1789 var headerEl = this.headerEl();
1792 headerEl.on('click', this.onToggleClick, this);
1797 this.el.on('click', this.onClick, this);
1802 onToggleClick : function()
1804 var headerEl = this.headerEl();
1820 if(this.fireEvent('expand', this)) {
1822 this.expanded = true;
1824 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1826 this.el.select('.panel-body',true).first().removeClass('hide');
1828 var toggleEl = this.toggleEl();
1834 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1839 collapse : function()
1841 if(this.fireEvent('collapse', this)) {
1843 this.expanded = false;
1845 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846 this.el.select('.panel-body',true).first().addClass('hide');
1848 var toggleEl = this.toggleEl();
1854 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1858 toggleEl : function()
1860 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1864 return this.el.select('.panel-heading .fa',true).first();
1867 headerEl : function()
1869 if(!this.el || !this.panel.length || !this.header.length){
1873 return this.el.select('.panel-heading',true).first()
1878 if(!this.el || !this.panel.length){
1882 return this.el.select('.panel-body',true).first()
1885 titleEl : function()
1887 if(!this.el || !this.panel.length || !this.header.length){
1891 return this.el.select('.panel-title',true).first();
1894 setTitle : function(v)
1896 var titleEl = this.titleEl();
1902 titleEl.dom.innerHTML = v;
1905 getTitle : function()
1908 var titleEl = this.titleEl();
1914 return titleEl.dom.innerHTML;
1917 setRightTitle : function(v)
1919 var t = this.el.select('.panel-header-right',true).first();
1925 t.dom.innerHTML = v;
1928 onClick : function(e)
1932 this.fireEvent('click', this, e);
1939 * This is BS4's Card element.. - similar to our containers probably..
1943 * @class Roo.bootstrap.Card
1944 * @extends Roo.bootstrap.Component
1945 * Bootstrap Card class
1948 * possible... may not be implemented..
1949 * @cfg {String} header_image src url of image.
1950 * @cfg {String|Object} header
1951 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1954 * @cfg {String} title
1955 * @cfg {String} subtitle
1956 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957 * @cfg {String} footer
1959 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1961 * @cfg {String} margin (0|1|2|3|4|5|auto)
1962 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1969 * @cfg {String} padding (0|1|2|3|4|5)
1970 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972 * @cfg {String} padding_left (0|1|2|3|4|5)
1973 * @cfg {String} padding_right (0|1|2|3|4|5)
1974 * @cfg {String} padding_x (0|1|2|3|4|5)
1975 * @cfg {String} padding_y (0|1|2|3|4|5)
1977 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @config {Boolean} dragable if this card can be dragged.
1984 * @config {String} drag_group group for drag
1985 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1986 * @config {String} drop_group group for drag
1988 * @config {Boolean} collapsable can the body be collapsed.
1989 * @config {Boolean} collapsed is the body collapsed when rendered...
1990 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991 * @config {Boolean} rotated is the body rotated when rendered...
1994 * Create a new Container
1995 * @param {Object} config The config object
1998 Roo.bootstrap.Card = function(config){
1999 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2005 * When a element a card is dropped
2006 * @param {Roo.bootstrap.Card} this
2009 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010 * @param {String} position 'above' or 'below'
2011 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2017 * When a element a card is rotate
2018 * @param {Roo.bootstrap.Element} this
2019 * @param {Roo.Element} n the node being dropped?
2020 * @param {Boolean} rotate status
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2033 margin: '', /// may be better in component?
2063 collapsable : false,
2072 childContainer : false,
2073 dropEl : false, /// the dom placeholde element that indicates drop location.
2074 containerEl: false, // body container
2075 bodyEl: false, // card-body
2076 headerContainerEl : false, //
2078 header_imageEl : false,
2080 layoutCls : function()
2084 Roo.log(this.margin_bottom.length);
2085 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2086 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2088 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2089 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2091 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2092 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2096 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2097 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2098 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2102 // more generic support?
2110 // Roo.log("Call onRender: " + this.xtype);
2111 /* We are looking at something like this.
2113 <img src="..." class="card-img-top" alt="...">
2114 <div class="card-body">
2115 <h5 class="card-title">Card title</h5>
2116 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2118 >> this bit is really the body...
2119 <div> << we will ad dthis in hopefully it will not break shit.
2121 ** card text does not actually have any styling...
2123 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2126 <a href="#" class="card-link">Card link</a>
2129 <div class="card-footer">
2130 <small class="text-muted">Last updated 3 mins ago</small>
2134 getAutoCreate : function(){
2142 if (this.weight.length && this.weight != 'light') {
2143 cfg.cls += ' text-white';
2145 cfg.cls += ' text-dark'; // need as it's nested..
2147 if (this.weight.length) {
2148 cfg.cls += ' bg-' + this.weight;
2151 cfg.cls += ' ' + this.layoutCls();
2154 var hdr_ctr = false;
2155 if (this.header.length) {
2157 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2158 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2172 if (this.collapsable) {
2175 cls : 'd-block user-select-none',
2179 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2184 hdr.cn.push(hdr_ctr);
2189 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2194 if (this.header_image.length) {
2197 cls : 'card-img-top',
2198 src: this.header_image // escape?
2203 cls : 'card-img-top d-none'
2209 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2213 if (this.collapsable || this.rotateable) {
2216 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2223 if (this.title.length) {
2227 src: this.title // escape?
2231 if (this.subtitle.length) {
2235 src: this.subtitle // escape?
2241 cls : 'roo-card-body-ctr'
2244 if (this.html.length) {
2250 // fixme ? handle objects?
2252 if (this.footer.length) {
2255 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2260 cfg.cn.push({cls : 'card-footer d-none'});
2269 getCardHeader : function()
2271 var ret = this.el.select('.card-header',true).first();
2272 if (ret.hasClass('d-none')) {
2273 ret.removeClass('d-none');
2278 getCardFooter : function()
2280 var ret = this.el.select('.card-footer',true).first();
2281 if (ret.hasClass('d-none')) {
2282 ret.removeClass('d-none');
2287 getCardImageTop : function()
2289 var ret = this.header_imageEl;
2290 if (ret.hasClass('d-none')) {
2291 ret.removeClass('d-none');
2297 getChildContainer : function()
2303 return this.el.select('.roo-card-body-ctr',true).first();
2306 initEvents: function()
2308 this.bodyEl = this.el.select('.card-body',true).first();
2309 this.containerEl = this.getChildContainer();
2311 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2312 containerScroll: true,
2313 ddGroup: this.drag_group || 'default_card_drag_group'
2315 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2317 if (this.dropable) {
2318 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2319 containerScroll: true,
2320 ddGroup: this.drop_group || 'default_card_drag_group'
2322 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2323 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2324 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2325 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2326 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2329 if (this.collapsable) {
2330 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2332 if (this.rotateable) {
2333 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2335 this.collapsableEl = this.el.select('.roo-collapsable').first();
2337 this.footerEl = this.el.select('.card-footer').first();
2338 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2339 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2340 this.headerEl = this.el.select('.card-header',true).first();
2343 this.el.addClass('roo-card-rotated');
2344 this.fireEvent('rotate', this, true);
2346 this.header_imageEl = this.el.select('.card-img-top',true).first();
2347 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2350 getDragData : function(e)
2352 var target = this.getEl();
2354 //this.handleSelection(e);
2359 nodes: this.getEl(),
2364 dragData.ddel = target.dom ; // the div element
2365 Roo.log(target.getWidth( ));
2366 dragData.ddel.style.width = target.getWidth() + 'px';
2373 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2374 * whole Element becomes the target, and this causes the drop gesture to append.
2376 getTargetFromEvent : function(e, dragged_card_el)
2378 var target = e.getTarget();
2379 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2380 target = target.parentNode;
2391 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2392 // see if target is one of the 'cards'...
2395 //Roo.log(this.items.length);
2398 var last_card_n = 0;
2400 for (var i = 0;i< this.items.length;i++) {
2402 if (!this.items[i].el.hasClass('card')) {
2405 pos = this.getDropPoint(e, this.items[i].el.dom);
2407 cards_len = ret.cards.length;
2408 //Roo.log(this.items[i].el.dom.id);
2409 ret.cards.push(this.items[i]);
2411 if (ret.card_n < 0 && pos == 'above') {
2412 ret.position = cards_len > 0 ? 'below' : pos;
2413 ret.items_n = i > 0 ? i - 1 : 0;
2414 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2415 ret.card = ret.cards[ret.card_n];
2418 if (!ret.cards.length) {
2420 ret.position = 'below';
2424 // could not find a card.. stick it at the end..
2425 if (ret.card_n < 0) {
2426 ret.card_n = last_card_n;
2427 ret.card = ret.cards[last_card_n];
2428 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2429 ret.position = 'below';
2432 if (this.items[ret.items_n].el == dragged_card_el) {
2436 if (ret.position == 'below') {
2437 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2439 if (card_after && card_after.el == dragged_card_el) {
2446 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2448 if (card_before && card_before.el == dragged_card_el) {
2455 onNodeEnter : function(n, dd, e, data){
2458 onNodeOver : function(n, dd, e, data)
2461 var target_info = this.getTargetFromEvent(e,data.source.el);
2462 if (target_info === false) {
2463 this.dropPlaceHolder('hide');
2466 Roo.log(['getTargetFromEvent', target_info ]);
2469 this.dropPlaceHolder('show', target_info,data);
2473 onNodeOut : function(n, dd, e, data){
2474 this.dropPlaceHolder('hide');
2477 onNodeDrop : function(n, dd, e, data)
2480 // call drop - return false if
2482 // this could actually fail - if the Network drops..
2483 // we will ignore this at present..- client should probably reload
2484 // the whole set of cards if stuff like that fails.
2487 var info = this.getTargetFromEvent(e,data.source.el);
2488 if (info === false) {
2491 this.dropPlaceHolder('hide');
2497 this.acceptCard(data.source, info.position, info.card, info.items_n);
2501 firstChildCard : function()
2503 for (var i = 0;i< this.items.length;i++) {
2505 if (!this.items[i].el.hasClass('card')) {
2508 return this.items[i];
2510 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2515 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2517 acceptCard : function(move_card, position, next_to_card )
2519 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2523 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2525 move_card.parent().removeCard(move_card);
2528 var dom = move_card.el.dom;
2529 dom.style.width = ''; // clear with - which is set by drag.
2531 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2532 var cardel = next_to_card.el.dom;
2534 if (position == 'above' ) {
2535 cardel.parentNode.insertBefore(dom, cardel);
2536 } else if (cardel.nextSibling) {
2537 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2539 cardel.parentNode.append(dom);
2542 // card container???
2543 this.containerEl.dom.append(dom);
2546 //FIXME HANDLE card = true
2548 // add this to the correct place in items.
2550 // remove Card from items.
2553 if (this.items.length) {
2555 //Roo.log([info.items_n, info.position, this.items.length]);
2556 for (var i =0; i < this.items.length; i++) {
2557 if (i == to_items_n && position == 'above') {
2558 nitems.push(move_card);
2560 nitems.push(this.items[i]);
2561 if (i == to_items_n && position == 'below') {
2562 nitems.push(move_card);
2565 this.items = nitems;
2566 Roo.log(this.items);
2568 this.items.push(move_card);
2571 move_card.parentId = this.id;
2577 removeCard : function(c)
2579 this.items = this.items.filter(function(e) { return e != c });
2582 dom.parentNode.removeChild(dom);
2583 dom.style.width = ''; // clear with - which is set by drag.
2588 /** Decide whether to drop above or below a View node. */
2589 getDropPoint : function(e, n, dd)
2594 if (n == this.containerEl.dom) {
2597 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2598 var c = t + (b - t) / 2;
2599 var y = Roo.lib.Event.getPageY(e);
2606 onToggleCollapse : function(e)
2608 if (this.collapsed) {
2609 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2610 this.collapsableEl.addClass('show');
2611 this.collapsed = false;
2614 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2615 this.collapsableEl.removeClass('show');
2616 this.collapsed = true;
2621 onToggleRotate : function(e)
2623 this.collapsableEl.removeClass('show');
2624 this.footerEl.removeClass('d-none');
2625 this.el.removeClass('roo-card-rotated');
2626 this.el.removeClass('d-none');
2629 this.collapsableEl.addClass('show');
2630 this.rotated = false;
2631 this.fireEvent('rotate', this, this.rotated);
2634 this.el.addClass('roo-card-rotated');
2635 this.footerEl.addClass('d-none');
2636 this.el.select('.roo-collapsable').removeClass('show');
2638 this.rotated = true;
2639 this.fireEvent('rotate', this, this.rotated);
2643 dropPlaceHolder: function (action, info, data)
2645 if (this.dropEl === false) {
2646 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2650 this.dropEl.removeClass(['d-none', 'd-block']);
2651 if (action == 'hide') {
2653 this.dropEl.addClass('d-none');
2656 // FIXME - info.card == true!!!
2657 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2659 if (info.card !== true) {
2660 var cardel = info.card.el.dom;
2662 if (info.position == 'above') {
2663 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2664 } else if (cardel.nextSibling) {
2665 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2667 cardel.parentNode.append(this.dropEl.dom);
2670 // card container???
2671 this.containerEl.dom.append(this.dropEl.dom);
2674 this.dropEl.addClass('d-block roo-card-dropzone');
2676 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2683 setHeaderText: function(html)
2686 if (this.headerContainerEl) {
2687 this.headerContainerEl.dom.innerHTML = html;
2690 onHeaderImageLoad : function(ev, he)
2692 if (!this.header_image_fit_square) {
2696 var hw = he.naturalHeight / he.naturalWidth;
2699 //var w = he.dom.naturalWidth;
2702 he.style.position = 'relative';
2704 var nw = (ww * (1/hw));
2705 Roo.get(he).setSize( ww * (1/hw), ww);
2706 he.style.left = ((ww - nw)/ 2) + 'px';
2707 he.style.position = 'relative';
2718 * Card header - holder for the card header elements.
2723 * @class Roo.bootstrap.CardHeader
2724 * @extends Roo.bootstrap.Element
2725 * Bootstrap CardHeader class
2727 * Create a new Card Header - that you can embed children into
2728 * @param {Object} config The config object
2731 Roo.bootstrap.CardHeader = function(config){
2732 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2735 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2738 container_method : 'getCardHeader'
2751 * Card footer - holder for the card footer elements.
2756 * @class Roo.bootstrap.CardFooter
2757 * @extends Roo.bootstrap.Element
2758 * Bootstrap CardFooter class
2760 * Create a new Card Footer - that you can embed children into
2761 * @param {Object} config The config object
2764 Roo.bootstrap.CardFooter = function(config){
2765 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2768 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2771 container_method : 'getCardFooter'
2784 * Card header - holder for the card header elements.
2789 * @class Roo.bootstrap.CardImageTop
2790 * @extends Roo.bootstrap.Element
2791 * Bootstrap CardImageTop class
2793 * Create a new Card Image Top container
2794 * @param {Object} config The config object
2797 Roo.bootstrap.CardImageTop = function(config){
2798 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2801 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2804 container_method : 'getCardImageTop'
2822 * @class Roo.bootstrap.Img
2823 * @extends Roo.bootstrap.Component
2824 * Bootstrap Img class
2825 * @cfg {Boolean} imgResponsive false | true
2826 * @cfg {String} border rounded | circle | thumbnail
2827 * @cfg {String} src image source
2828 * @cfg {String} alt image alternative text
2829 * @cfg {String} href a tag href
2830 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2831 * @cfg {String} xsUrl xs image source
2832 * @cfg {String} smUrl sm image source
2833 * @cfg {String} mdUrl md image source
2834 * @cfg {String} lgUrl lg image source
2837 * Create a new Input
2838 * @param {Object} config The config object
2841 Roo.bootstrap.Img = function(config){
2842 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2848 * The img click event for the img.
2849 * @param {Roo.EventObject} e
2855 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2857 imgResponsive: true,
2867 getAutoCreate : function()
2869 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2870 return this.createSingleImg();
2875 cls: 'roo-image-responsive-group',
2880 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2882 if(!_this[size + 'Url']){
2888 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2889 html: _this.html || cfg.html,
2890 src: _this[size + 'Url']
2893 img.cls += ' roo-image-responsive-' + size;
2895 var s = ['xs', 'sm', 'md', 'lg'];
2897 s.splice(s.indexOf(size), 1);
2899 Roo.each(s, function(ss){
2900 img.cls += ' hidden-' + ss;
2903 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2904 cfg.cls += ' img-' + _this.border;
2908 cfg.alt = _this.alt;
2921 a.target = _this.target;
2925 cfg.cn.push((_this.href) ? a : img);
2932 createSingleImg : function()
2936 cls: (this.imgResponsive) ? 'img-responsive' : '',
2938 src : 'about:blank' // just incase src get's set to undefined?!?
2941 cfg.html = this.html || cfg.html;
2943 cfg.src = this.src || cfg.src;
2945 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2946 cfg.cls += ' img-' + this.border;
2963 a.target = this.target;
2968 return (this.href) ? a : cfg;
2971 initEvents: function()
2974 this.el.on('click', this.onClick, this);
2979 onClick : function(e)
2981 Roo.log('img onclick');
2982 this.fireEvent('click', this, e);
2985 * Sets the url of the image - used to update it
2986 * @param {String} url the url of the image
2989 setSrc : function(url)
2993 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2994 this.el.dom.src = url;
2998 this.el.select('img', true).first().dom.src = url;
3014 * @class Roo.bootstrap.Link
3015 * @extends Roo.bootstrap.Component
3016 * Bootstrap Link Class
3017 * @cfg {String} alt image alternative text
3018 * @cfg {String} href a tag href
3019 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3020 * @cfg {String} html the content of the link.
3021 * @cfg {String} anchor name for the anchor link
3022 * @cfg {String} fa - favicon
3024 * @cfg {Boolean} preventDefault (true | false) default false
3028 * Create a new Input
3029 * @param {Object} config The config object
3032 Roo.bootstrap.Link = function(config){
3033 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3039 * The img click event for the img.
3040 * @param {Roo.EventObject} e
3046 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3050 preventDefault: false,
3056 getAutoCreate : function()
3058 var html = this.html || '';
3060 if (this.fa !== false) {
3061 html = '<i class="fa fa-' + this.fa + '"></i>';
3066 // anchor's do not require html/href...
3067 if (this.anchor === false) {
3069 cfg.href = this.href || '#';
3071 cfg.name = this.anchor;
3072 if (this.html !== false || this.fa !== false) {
3075 if (this.href !== false) {
3076 cfg.href = this.href;
3080 if(this.alt !== false){
3085 if(this.target !== false) {
3086 cfg.target = this.target;
3092 initEvents: function() {
3094 if(!this.href || this.preventDefault){
3095 this.el.on('click', this.onClick, this);
3099 onClick : function(e)
3101 if(this.preventDefault){
3104 //Roo.log('img onclick');
3105 this.fireEvent('click', this, e);
3118 * @class Roo.bootstrap.Header
3119 * @extends Roo.bootstrap.Component
3120 * Bootstrap Header class
3121 * @cfg {String} html content of header
3122 * @cfg {Number} level (1|2|3|4|5|6) default 1
3125 * Create a new Header
3126 * @param {Object} config The config object
3130 Roo.bootstrap.Header = function(config){
3131 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3134 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3142 getAutoCreate : function(){
3147 tag: 'h' + (1 *this.level),
3148 html: this.html || ''
3160 * Ext JS Library 1.1.1
3161 * Copyright(c) 2006-2007, Ext JS, LLC.
3163 * Originally Released Under LGPL - original licence link has changed is not relivant.
3166 * <script type="text/javascript">
3170 * @class Roo.bootstrap.MenuMgr
3171 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3174 Roo.bootstrap.MenuMgr = function(){
3175 var menus, active, groups = {}, attached = false, lastShow = new Date();
3177 // private - called when first menu is created
3180 active = new Roo.util.MixedCollection();
3181 Roo.get(document).addKeyListener(27, function(){
3182 if(active.length > 0){
3190 if(active && active.length > 0){
3191 var c = active.clone();
3201 if(active.length < 1){
3202 Roo.get(document).un("mouseup", onMouseDown);
3210 var last = active.last();
3211 lastShow = new Date();
3214 Roo.get(document).on("mouseup", onMouseDown);
3219 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3220 m.parentMenu.activeChild = m;
3221 }else if(last && last.isVisible()){
3222 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3227 function onBeforeHide(m){
3229 m.activeChild.hide();
3231 if(m.autoHideTimer){
3232 clearTimeout(m.autoHideTimer);
3233 delete m.autoHideTimer;
3238 function onBeforeShow(m){
3239 var pm = m.parentMenu;
3240 if(!pm && !m.allowOtherMenus){
3242 }else if(pm && pm.activeChild && active != m){
3243 pm.activeChild.hide();
3247 // private this should really trigger on mouseup..
3248 function onMouseDown(e){
3249 Roo.log("on Mouse Up");
3251 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3252 Roo.log("MenuManager hideAll");
3261 function onBeforeCheck(mi, state){
3263 var g = groups[mi.group];
3264 for(var i = 0, l = g.length; i < l; i++){
3266 g[i].setChecked(false);
3275 * Hides all menus that are currently visible
3277 hideAll : function(){
3282 register : function(menu){
3286 menus[menu.id] = menu;
3287 menu.on("beforehide", onBeforeHide);
3288 menu.on("hide", onHide);
3289 menu.on("beforeshow", onBeforeShow);
3290 menu.on("show", onShow);
3292 if(g && menu.events["checkchange"]){
3296 groups[g].push(menu);
3297 menu.on("checkchange", onCheck);
3302 * Returns a {@link Roo.menu.Menu} object
3303 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3304 * be used to generate and return a new Menu instance.
3306 get : function(menu){
3307 if(typeof menu == "string"){ // menu id
3309 }else if(menu.events){ // menu instance
3312 /*else if(typeof menu.length == 'number'){ // array of menu items?
3313 return new Roo.bootstrap.Menu({items:menu});
3314 }else{ // otherwise, must be a config
3315 return new Roo.bootstrap.Menu(menu);
3322 unregister : function(menu){
3323 delete menus[menu.id];
3324 menu.un("beforehide", onBeforeHide);
3325 menu.un("hide", onHide);
3326 menu.un("beforeshow", onBeforeShow);
3327 menu.un("show", onShow);
3329 if(g && menu.events["checkchange"]){
3330 groups[g].remove(menu);
3331 menu.un("checkchange", onCheck);
3336 registerCheckable : function(menuItem){
3337 var g = menuItem.group;
3342 groups[g].push(menuItem);
3343 menuItem.on("beforecheckchange", onBeforeCheck);
3348 unregisterCheckable : function(menuItem){
3349 var g = menuItem.group;
3351 groups[g].remove(menuItem);
3352 menuItem.un("beforecheckchange", onBeforeCheck);
3364 * @class Roo.bootstrap.Menu
3365 * @extends Roo.bootstrap.Component
3366 * Bootstrap Menu class - container for MenuItems
3367 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3368 * @cfg {bool} hidden if the menu should be hidden when rendered.
3369 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3370 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3374 * @param {Object} config The config object
3378 Roo.bootstrap.Menu = function(config){
3379 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3380 if (this.registerMenu && this.type != 'treeview') {
3381 Roo.bootstrap.MenuMgr.register(this);
3388 * Fires before this menu is displayed (return false to block)
3389 * @param {Roo.menu.Menu} this
3394 * Fires before this menu is hidden (return false to block)
3395 * @param {Roo.menu.Menu} this
3400 * Fires after this menu is displayed
3401 * @param {Roo.menu.Menu} this
3406 * Fires after this menu is hidden
3407 * @param {Roo.menu.Menu} this
3412 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3413 * @param {Roo.menu.Menu} this
3414 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3415 * @param {Roo.EventObject} e
3420 * Fires when the mouse is hovering over this menu
3421 * @param {Roo.menu.Menu} this
3422 * @param {Roo.EventObject} e
3423 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3428 * Fires when the mouse exits this menu
3429 * @param {Roo.menu.Menu} this
3430 * @param {Roo.EventObject} e
3431 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3436 * Fires when a menu item contained in this menu is clicked
3437 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3438 * @param {Roo.EventObject} e
3442 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3445 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3449 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3452 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3454 registerMenu : true,
3456 menuItems :false, // stores the menu items..
3466 getChildContainer : function() {
3470 getAutoCreate : function(){
3472 //if (['right'].indexOf(this.align)!==-1) {
3473 // cfg.cn[1].cls += ' pull-right'
3479 cls : 'dropdown-menu' ,
3480 style : 'z-index:1000'
3484 if (this.type === 'submenu') {
3485 cfg.cls = 'submenu active';
3487 if (this.type === 'treeview') {
3488 cfg.cls = 'treeview-menu';
3493 initEvents : function() {
3495 // Roo.log("ADD event");
3496 // Roo.log(this.triggerEl.dom);
3498 this.triggerEl.on('click', this.onTriggerClick, this);
3500 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3503 if (this.triggerEl.hasClass('nav-item')) {
3504 // dropdown toggle on the 'a' in BS4?
3505 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3507 this.triggerEl.addClass('dropdown-toggle');
3510 this.el.on('touchstart' , this.onTouch, this);
3512 this.el.on('click' , this.onClick, this);
3514 this.el.on("mouseover", this.onMouseOver, this);
3515 this.el.on("mouseout", this.onMouseOut, this);
3519 findTargetItem : function(e)
3521 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3525 //Roo.log(t); Roo.log(t.id);
3527 //Roo.log(this.menuitems);
3528 return this.menuitems.get(t.id);
3530 //return this.items.get(t.menuItemId);
3536 onTouch : function(e)
3538 Roo.log("menu.onTouch");
3539 //e.stopEvent(); this make the user popdown broken
3543 onClick : function(e)
3545 Roo.log("menu.onClick");
3547 var t = this.findTargetItem(e);
3548 if(!t || t.isContainer){
3553 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3554 if(t == this.activeItem && t.shouldDeactivate(e)){
3555 this.activeItem.deactivate();
3556 delete this.activeItem;
3560 this.setActiveItem(t, true);
3568 Roo.log('pass click event');
3572 this.fireEvent("click", this, t, e);
3576 if(!t.href.length || t.href == '#'){
3577 (function() { _this.hide(); }).defer(100);
3582 onMouseOver : function(e){
3583 var t = this.findTargetItem(e);
3586 // if(t.canActivate && !t.disabled){
3587 // this.setActiveItem(t, true);
3591 this.fireEvent("mouseover", this, e, t);
3593 isVisible : function(){
3594 return !this.hidden;
3596 onMouseOut : function(e){
3597 var t = this.findTargetItem(e);
3600 // if(t == this.activeItem && t.shouldDeactivate(e)){
3601 // this.activeItem.deactivate();
3602 // delete this.activeItem;
3605 this.fireEvent("mouseout", this, e, t);
3610 * Displays this menu relative to another element
3611 * @param {String/HTMLElement/Roo.Element} element The element to align to
3612 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3613 * the element (defaults to this.defaultAlign)
3614 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3616 show : function(el, pos, parentMenu)
3618 if (false === this.fireEvent("beforeshow", this)) {
3619 Roo.log("show canceled");
3622 this.parentMenu = parentMenu;
3627 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3630 * Displays this menu at a specific xy position
3631 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3632 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3634 showAt : function(xy, parentMenu, /* private: */_e){
3635 this.parentMenu = parentMenu;
3640 this.fireEvent("beforeshow", this);
3641 //xy = this.el.adjustForConstraints(xy);
3645 this.hideMenuItems();
3646 this.hidden = false;
3647 this.triggerEl.addClass('open');
3648 this.el.addClass('show');
3650 // reassign x when hitting right
3651 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3652 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3655 // reassign y when hitting bottom
3656 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3657 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3660 // but the list may align on trigger left or trigger top... should it be a properity?
3662 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3667 this.fireEvent("show", this);
3673 this.doFocus.defer(50, this);
3677 doFocus : function(){
3679 this.focusEl.focus();
3684 * Hides this menu and optionally all parent menus
3685 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3687 hide : function(deep)
3689 if (false === this.fireEvent("beforehide", this)) {
3690 Roo.log("hide canceled");
3693 this.hideMenuItems();
3694 if(this.el && this.isVisible()){
3696 if(this.activeItem){
3697 this.activeItem.deactivate();
3698 this.activeItem = null;
3700 this.triggerEl.removeClass('open');;
3701 this.el.removeClass('show');
3703 this.fireEvent("hide", this);
3705 if(deep === true && this.parentMenu){
3706 this.parentMenu.hide(true);
3710 onTriggerClick : function(e)
3712 Roo.log('trigger click');
3714 var target = e.getTarget();
3716 Roo.log(target.nodeName.toLowerCase());
3718 if(target.nodeName.toLowerCase() === 'i'){
3724 onTriggerPress : function(e)
3726 Roo.log('trigger press');
3727 //Roo.log(e.getTarget());
3728 // Roo.log(this.triggerEl.dom);
3730 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3731 var pel = Roo.get(e.getTarget());
3732 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3733 Roo.log('is treeview or dropdown?');
3737 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3741 if (this.isVisible()) {
3746 this.show(this.triggerEl, '?', false);
3749 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3756 hideMenuItems : function()
3758 Roo.log("hide Menu Items");
3763 this.el.select('.open',true).each(function(aa) {
3765 aa.removeClass('open');
3769 addxtypeChild : function (tree, cntr) {
3770 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3772 this.menuitems.add(comp);
3784 this.getEl().dom.innerHTML = '';
3785 this.menuitems.clear();
3799 * @class Roo.bootstrap.MenuItem
3800 * @extends Roo.bootstrap.Component
3801 * Bootstrap MenuItem class
3802 * @cfg {String} html the menu label
3803 * @cfg {String} href the link
3804 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3805 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3806 * @cfg {Boolean} active used on sidebars to highlight active itesm
3807 * @cfg {String} fa favicon to show on left of menu item.
3808 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3812 * Create a new MenuItem
3813 * @param {Object} config The config object
3817 Roo.bootstrap.MenuItem = function(config){
3818 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3823 * The raw click event for the entire grid.
3824 * @param {Roo.bootstrap.MenuItem} this
3825 * @param {Roo.EventObject} e
3831 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3835 preventDefault: false,
3836 isContainer : false,
3840 getAutoCreate : function(){
3842 if(this.isContainer){
3845 cls: 'dropdown-menu-item '
3855 cls : 'dropdown-item',
3860 if (this.fa !== false) {
3863 cls : 'fa fa-' + this.fa
3872 cls: 'dropdown-menu-item',
3875 if (this.parent().type == 'treeview') {
3876 cfg.cls = 'treeview-menu';
3879 cfg.cls += ' active';
3884 anc.href = this.href || cfg.cn[0].href ;
3885 ctag.html = this.html || cfg.cn[0].html ;
3889 initEvents: function()
3891 if (this.parent().type == 'treeview') {
3892 this.el.select('a').on('click', this.onClick, this);
3896 this.menu.parentType = this.xtype;
3897 this.menu.triggerEl = this.el;
3898 this.menu = this.addxtype(Roo.apply({}, this.menu));
3902 onClick : function(e)
3904 Roo.log('item on click ');
3906 if(this.preventDefault){
3909 //this.parent().hideMenuItems();
3911 this.fireEvent('click', this, e);
3930 * @class Roo.bootstrap.MenuSeparator
3931 * @extends Roo.bootstrap.Component
3932 * Bootstrap MenuSeparator class
3935 * Create a new MenuItem
3936 * @param {Object} config The config object
3940 Roo.bootstrap.MenuSeparator = function(config){
3941 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3944 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3946 getAutoCreate : function(){
3965 * @class Roo.bootstrap.Modal
3966 * @extends Roo.bootstrap.Component
3967 * Bootstrap Modal class
3968 * @cfg {String} title Title of dialog
3969 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3970 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3971 * @cfg {Boolean} specificTitle default false
3972 * @cfg {Array} buttons Array of buttons or standard button set..
3973 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3974 * @cfg {Boolean} animate default true
3975 * @cfg {Boolean} allow_close default true
3976 * @cfg {Boolean} fitwindow default false
3977 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3978 * @cfg {Number} width fixed width - usefull for chrome extension only really.
3979 * @cfg {Number} height fixed height - usefull for chrome extension only really.
3980 * @cfg {String} size (sm|lg|xl) default empty
3981 * @cfg {Number} max_width set the max width of modal
3982 * @cfg {Boolean} editableTitle can the title be edited
3987 * Create a new Modal Dialog
3988 * @param {Object} config The config object
3991 Roo.bootstrap.Modal = function(config){
3992 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3997 * The raw btnclick event for the button
3998 * @param {Roo.EventObject} e
4003 * Fire when dialog resize
4004 * @param {Roo.bootstrap.Modal} this
4005 * @param {Roo.EventObject} e
4009 * @event titlechanged
4010 * Fire when the editable title has been changed
4011 * @param {Roo.bootstrap.Modal} this
4012 * @param {Roo.EventObject} value
4014 "titlechanged" : true
4017 this.buttons = this.buttons || [];
4020 this.tmpl = Roo.factory(this.tmpl);
4025 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4027 title : 'test dialog',
4037 specificTitle: false,
4039 buttonPosition: 'right',
4061 editableTitle : false,
4063 onRender : function(ct, position)
4065 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4068 var cfg = Roo.apply({}, this.getAutoCreate());
4071 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4073 //if (!cfg.name.length) {
4077 cfg.cls += ' ' + this.cls;
4080 cfg.style = this.style;
4082 this.el = Roo.get(document.body).createChild(cfg, position);
4084 //var type = this.el.dom.type;
4087 if(this.tabIndex !== undefined){
4088 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4091 this.dialogEl = this.el.select('.modal-dialog',true).first();
4092 this.bodyEl = this.el.select('.modal-body',true).first();
4093 this.closeEl = this.el.select('.modal-header .close', true).first();
4094 this.headerEl = this.el.select('.modal-header',true).first();
4095 this.titleEl = this.el.select('.modal-title',true).first();
4096 this.footerEl = this.el.select('.modal-footer',true).first();
4098 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4100 //this.el.addClass("x-dlg-modal");
4102 if (this.buttons.length) {
4103 Roo.each(this.buttons, function(bb) {
4104 var b = Roo.apply({}, bb);
4105 b.xns = b.xns || Roo.bootstrap;
4106 b.xtype = b.xtype || 'Button';
4107 if (typeof(b.listeners) == 'undefined') {
4108 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4111 var btn = Roo.factory(b);
4113 btn.render(this.getButtonContainer());
4117 // render the children.
4120 if(typeof(this.items) != 'undefined'){
4121 var items = this.items;
4124 for(var i =0;i < items.length;i++) {
4125 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4129 this.items = nitems;
4131 // where are these used - they used to be body/close/footer
4135 //this.el.addClass([this.fieldClass, this.cls]);
4139 getAutoCreate : function()
4141 // we will default to modal-body-overflow - might need to remove or make optional later.
4143 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4144 html : this.html || ''
4149 cls : 'modal-title',
4153 if(this.specificTitle){ // WTF is this?
4158 if (this.allow_close && Roo.bootstrap.version == 3) {
4168 if (this.editableTitle) {
4170 cls: 'form-control roo-editable-title d-none',
4176 if (this.allow_close && Roo.bootstrap.version == 4) {
4186 if(this.size.length){
4187 size = 'modal-' + this.size;
4190 var footer = Roo.bootstrap.version == 3 ?
4192 cls : 'modal-footer',
4196 cls: 'btn-' + this.buttonPosition
4201 { // BS4 uses mr-auto on left buttons....
4202 cls : 'modal-footer'
4213 cls: "modal-dialog " + size,
4216 cls : "modal-content",
4219 cls : 'modal-header',
4234 modal.cls += ' fade';
4240 getChildContainer : function() {
4245 getButtonContainer : function() {
4247 return Roo.bootstrap.version == 4 ?
4248 this.el.select('.modal-footer',true).first()
4249 : this.el.select('.modal-footer div',true).first();
4252 initEvents : function()
4254 if (this.allow_close) {
4255 this.closeEl.on('click', this.hide, this);
4257 Roo.EventManager.onWindowResize(this.resize, this, true);
4258 if (this.editableTitle) {
4259 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4260 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4261 this.headerEditEl.on('keyup', function(e) {
4262 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4263 this.toggleHeaderInput(false)
4266 this.headerEditEl.on('blur', function(e) {
4267 this.toggleHeaderInput(false)
4276 this.maskEl.setSize(
4277 Roo.lib.Dom.getViewWidth(true),
4278 Roo.lib.Dom.getViewHeight(true)
4281 if (this.fitwindow) {
4283 this.dialogEl.setStyle( { 'max-width' : '100%' });
4285 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4286 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4291 if(this.max_width !== 0) {
4293 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4296 this.setSize(w, this.height);
4300 if(this.max_height) {
4301 this.setSize(w,Math.min(
4303 Roo.lib.Dom.getViewportHeight(true) - 60
4309 if(!this.fit_content) {
4310 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4314 this.setSize(w, Math.min(
4316 this.headerEl.getHeight() +
4317 this.footerEl.getHeight() +
4318 this.getChildHeight(this.bodyEl.dom.childNodes),
4319 Roo.lib.Dom.getViewportHeight(true) - 60)
4325 setSize : function(w,h)
4336 if (!this.rendered) {
4339 this.toggleHeaderInput(false);
4340 //this.el.setStyle('display', 'block');
4341 this.el.removeClass('hideing');
4342 this.el.dom.style.display='block';
4344 Roo.get(document.body).addClass('modal-open');
4346 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4349 this.el.addClass('show');
4350 this.el.addClass('in');
4353 this.el.addClass('show');
4354 this.el.addClass('in');
4357 // not sure how we can show data in here..
4359 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4362 Roo.get(document.body).addClass("x-body-masked");
4364 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4365 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4366 this.maskEl.dom.style.display = 'block';
4367 this.maskEl.addClass('show');
4372 this.fireEvent('show', this);
4374 // set zindex here - otherwise it appears to be ignored...
4375 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4378 this.items.forEach( function(e) {
4379 e.layout ? e.layout() : false;
4387 if(this.fireEvent("beforehide", this) !== false){
4389 this.maskEl.removeClass('show');
4391 this.maskEl.dom.style.display = '';
4392 Roo.get(document.body).removeClass("x-body-masked");
4393 this.el.removeClass('in');
4394 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4396 if(this.animate){ // why
4397 this.el.addClass('hideing');
4398 this.el.removeClass('show');
4400 if (!this.el.hasClass('hideing')) {
4401 return; // it's been shown again...
4404 this.el.dom.style.display='';
4406 Roo.get(document.body).removeClass('modal-open');
4407 this.el.removeClass('hideing');
4411 this.el.removeClass('show');
4412 this.el.dom.style.display='';
4413 Roo.get(document.body).removeClass('modal-open');
4416 this.fireEvent('hide', this);
4419 isVisible : function()
4422 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4426 addButton : function(str, cb)
4430 var b = Roo.apply({}, { html : str } );
4431 b.xns = b.xns || Roo.bootstrap;
4432 b.xtype = b.xtype || 'Button';
4433 if (typeof(b.listeners) == 'undefined') {
4434 b.listeners = { click : cb.createDelegate(this) };
4437 var btn = Roo.factory(b);
4439 btn.render(this.getButtonContainer());
4445 setDefaultButton : function(btn)
4447 //this.el.select('.modal-footer').()
4450 resizeTo: function(w,h)
4452 this.dialogEl.setWidth(w);
4454 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4456 this.bodyEl.setHeight(h - diff);
4458 this.fireEvent('resize', this);
4461 setContentSize : function(w, h)
4465 onButtonClick: function(btn,e)
4468 this.fireEvent('btnclick', btn.name, e);
4471 * Set the title of the Dialog
4472 * @param {String} str new Title
4474 setTitle: function(str) {
4475 this.titleEl.dom.innerHTML = str;
4479 * Set the body of the Dialog
4480 * @param {String} str new Title
4482 setBody: function(str) {
4483 this.bodyEl.dom.innerHTML = str;
4486 * Set the body of the Dialog using the template
4487 * @param {Obj} data - apply this data to the template and replace the body contents.
4489 applyBody: function(obj)
4492 Roo.log("Error - using apply Body without a template");
4495 this.tmpl.overwrite(this.bodyEl, obj);
4498 getChildHeight : function(child_nodes)
4502 child_nodes.length == 0
4507 var child_height = 0;
4509 for(var i = 0; i < child_nodes.length; i++) {
4512 * for modal with tabs...
4513 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4515 var layout_childs = child_nodes[i].childNodes;
4517 for(var j = 0; j < layout_childs.length; j++) {
4519 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4521 var layout_body_childs = layout_childs[j].childNodes;
4523 for(var k = 0; k < layout_body_childs.length; k++) {
4525 if(layout_body_childs[k].classList.contains('navbar')) {
4526 child_height += layout_body_childs[k].offsetHeight;
4530 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4532 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4534 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4536 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4537 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4552 child_height += child_nodes[i].offsetHeight;
4553 // Roo.log(child_nodes[i].offsetHeight);
4556 return child_height;
4558 toggleHeaderInput : function(is_edit)
4560 if (!this.editableTitle) {
4561 return; // not editable.
4563 if (is_edit && this.is_header_editing) {
4564 return; // already editing..
4568 this.headerEditEl.dom.value = this.title;
4569 this.headerEditEl.removeClass('d-none');
4570 this.headerEditEl.dom.focus();
4571 this.titleEl.addClass('d-none');
4573 this.is_header_editing = true;
4576 // flip back to not editing.
4577 this.title = this.headerEditEl.dom.value;
4578 this.headerEditEl.addClass('d-none');
4579 this.titleEl.removeClass('d-none');
4580 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4581 this.is_header_editing = false;
4582 this.fireEvent('titlechanged', this, this.title);
4591 Roo.apply(Roo.bootstrap.Modal, {
4593 * Button config that displays a single OK button
4602 * Button config that displays Yes and No buttons
4618 * Button config that displays OK and Cancel buttons
4633 * Button config that displays Yes, No and Cancel buttons
4658 * messagebox - can be used as a replace
4662 * @class Roo.MessageBox
4663 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4667 Roo.Msg.alert('Status', 'Changes saved successfully.');
4669 // Prompt for user data:
4670 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4672 // process text value...
4676 // Show a dialog using config options:
4678 title:'Save Changes?',
4679 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4680 buttons: Roo.Msg.YESNOCANCEL,
4687 Roo.bootstrap.MessageBox = function(){
4688 var dlg, opt, mask, waitTimer;
4689 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4690 var buttons, activeTextEl, bwidth;
4694 var handleButton = function(button){
4696 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4700 var handleHide = function(){
4702 dlg.el.removeClass(opt.cls);
4705 // Roo.TaskMgr.stop(waitTimer);
4706 // waitTimer = null;
4711 var updateButtons = function(b){
4714 buttons["ok"].hide();
4715 buttons["cancel"].hide();
4716 buttons["yes"].hide();
4717 buttons["no"].hide();
4718 dlg.footerEl.hide();
4722 dlg.footerEl.show();
4723 for(var k in buttons){
4724 if(typeof buttons[k] != "function"){
4727 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4728 width += buttons[k].el.getWidth()+15;
4738 var handleEsc = function(d, k, e){
4739 if(opt && opt.closable !== false){
4749 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4750 * @return {Roo.BasicDialog} The BasicDialog element
4752 getDialog : function(){
4754 dlg = new Roo.bootstrap.Modal( {
4757 //constraintoviewport:false,
4759 //collapsible : false,
4764 //buttonAlign:"center",
4765 closeClick : function(){
4766 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4769 handleButton("cancel");
4774 dlg.on("hide", handleHide);
4776 //dlg.addKeyListener(27, handleEsc);
4778 this.buttons = buttons;
4779 var bt = this.buttonText;
4780 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4781 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4782 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4783 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4785 bodyEl = dlg.bodyEl.createChild({
4787 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4788 '<textarea class="roo-mb-textarea"></textarea>' +
4789 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4791 msgEl = bodyEl.dom.firstChild;
4792 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4793 textboxEl.enableDisplayMode();
4794 textboxEl.addKeyListener([10,13], function(){
4795 if(dlg.isVisible() && opt && opt.buttons){
4798 }else if(opt.buttons.yes){
4799 handleButton("yes");
4803 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4804 textareaEl.enableDisplayMode();
4805 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4806 progressEl.enableDisplayMode();
4808 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4809 var pf = progressEl.dom.firstChild;
4811 pp = Roo.get(pf.firstChild);
4812 pp.setHeight(pf.offsetHeight);
4820 * Updates the message box body text
4821 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4822 * the XHTML-compliant non-breaking space character '&#160;')
4823 * @return {Roo.MessageBox} This message box
4825 updateText : function(text)
4827 if(!dlg.isVisible() && !opt.width){
4828 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4829 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4831 msgEl.innerHTML = text || ' ';
4833 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4834 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4836 Math.min(opt.width || cw , this.maxWidth),
4837 Math.max(opt.minWidth || this.minWidth, bwidth)
4840 activeTextEl.setWidth(w);
4842 if(dlg.isVisible()){
4843 dlg.fixedcenter = false;
4845 // to big, make it scroll. = But as usual stupid IE does not support
4848 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4849 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4850 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4852 bodyEl.dom.style.height = '';
4853 bodyEl.dom.style.overflowY = '';
4856 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4858 bodyEl.dom.style.overflowX = '';
4861 dlg.setContentSize(w, bodyEl.getHeight());
4862 if(dlg.isVisible()){
4863 dlg.fixedcenter = true;
4869 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4870 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4871 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4872 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4873 * @return {Roo.MessageBox} This message box
4875 updateProgress : function(value, text){
4877 this.updateText(text);
4880 if (pp) { // weird bug on my firefox - for some reason this is not defined
4881 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4882 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4888 * Returns true if the message box is currently displayed
4889 * @return {Boolean} True if the message box is visible, else false
4891 isVisible : function(){
4892 return dlg && dlg.isVisible();
4896 * Hides the message box if it is displayed
4899 if(this.isVisible()){
4905 * Displays a new message box, or reinitializes an existing message box, based on the config options
4906 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4907 * The following config object properties are supported:
4909 Property Type Description
4910 ---------- --------------- ------------------------------------------------------------------------------------
4911 animEl String/Element An id or Element from which the message box should animate as it opens and
4912 closes (defaults to undefined)
4913 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4914 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4915 closable Boolean False to hide the top-right close button (defaults to true). Note that
4916 progress and wait dialogs will ignore this property and always hide the
4917 close button as they can only be closed programmatically.
4918 cls String A custom CSS class to apply to the message box element
4919 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4920 displayed (defaults to 75)
4921 fn Function A callback function to execute after closing the dialog. The arguments to the
4922 function will be btn (the name of the button that was clicked, if applicable,
4923 e.g. "ok"), and text (the value of the active text field, if applicable).
4924 Progress and wait dialogs will ignore this option since they do not respond to
4925 user actions and can only be closed programmatically, so any required function
4926 should be called by the same code after it closes the dialog.
4927 icon String A CSS class that provides a background image to be used as an icon for
4928 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4929 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4930 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4931 modal Boolean False to allow user interaction with the page while the message box is
4932 displayed (defaults to true)
4933 msg String A string that will replace the existing message box body text (defaults
4934 to the XHTML-compliant non-breaking space character ' ')
4935 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4936 progress Boolean True to display a progress bar (defaults to false)
4937 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4938 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4939 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4940 title String The title text
4941 value String The string value to set into the active textbox element if displayed
4942 wait Boolean True to display a progress bar (defaults to false)
4943 width Number The width of the dialog in pixels
4950 msg: 'Please enter your address:',
4952 buttons: Roo.MessageBox.OKCANCEL,
4955 animEl: 'addAddressBtn'
4958 * @param {Object} config Configuration options
4959 * @return {Roo.MessageBox} This message box
4961 show : function(options)
4964 // this causes nightmares if you show one dialog after another
4965 // especially on callbacks..
4967 if(this.isVisible()){
4970 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4971 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4972 Roo.log("New Dialog Message:" + options.msg )
4973 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4974 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4977 var d = this.getDialog();
4979 d.setTitle(opt.title || " ");
4980 d.closeEl.setDisplayed(opt.closable !== false);
4981 activeTextEl = textboxEl;
4982 opt.prompt = opt.prompt || (opt.multiline ? true : false);
4987 textareaEl.setHeight(typeof opt.multiline == "number" ?
4988 opt.multiline : this.defaultTextHeight);
4989 activeTextEl = textareaEl;
4998 progressEl.setDisplayed(opt.progress === true);
5000 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5002 this.updateProgress(0);
5003 activeTextEl.dom.value = opt.value || "";
5005 dlg.setDefaultButton(activeTextEl);
5007 var bs = opt.buttons;
5011 }else if(bs && bs.yes){
5012 db = buttons["yes"];
5014 dlg.setDefaultButton(db);
5016 bwidth = updateButtons(opt.buttons);
5017 this.updateText(opt.msg);
5019 d.el.addClass(opt.cls);
5021 d.proxyDrag = opt.proxyDrag === true;
5022 d.modal = opt.modal !== false;
5023 d.mask = opt.modal !== false ? mask : false;
5025 // force it to the end of the z-index stack so it gets a cursor in FF
5026 document.body.appendChild(dlg.el.dom);
5027 d.animateTarget = null;
5028 d.show(options.animEl);
5034 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5035 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5036 * and closing the message box when the process is complete.
5037 * @param {String} title The title bar text
5038 * @param {String} msg The message box body text
5039 * @return {Roo.MessageBox} This message box
5041 progress : function(title, msg){
5048 minWidth: this.minProgressWidth,
5055 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5056 * If a callback function is passed it will be called after the user clicks the button, and the
5057 * id of the button that was clicked will be passed as the only parameter to the callback
5058 * (could also be the top-right close button).
5059 * @param {String} title The title bar text
5060 * @param {String} msg The message box body text
5061 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5062 * @param {Object} scope (optional) The scope of the callback function
5063 * @return {Roo.MessageBox} This message box
5065 alert : function(title, msg, fn, scope)
5080 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5081 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5082 * You are responsible for closing the message box when the process is complete.
5083 * @param {String} msg The message box body text
5084 * @param {String} title (optional) The title bar text
5085 * @return {Roo.MessageBox} This message box
5087 wait : function(msg, title){
5098 waitTimer = Roo.TaskMgr.start({
5100 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5108 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5109 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5110 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5111 * @param {String} title The title bar text
5112 * @param {String} msg The message box body text
5113 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114 * @param {Object} scope (optional) The scope of the callback function
5115 * @return {Roo.MessageBox} This message box
5117 confirm : function(title, msg, fn, scope){
5121 buttons: this.YESNO,
5130 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5131 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5132 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5133 * (could also be the top-right close button) and the text that was entered will be passed as the two
5134 * parameters to the callback.
5135 * @param {String} title The title bar text
5136 * @param {String} msg The message box body text
5137 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5138 * @param {Object} scope (optional) The scope of the callback function
5139 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5140 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5141 * @return {Roo.MessageBox} This message box
5143 prompt : function(title, msg, fn, scope, multiline){
5147 buttons: this.OKCANCEL,
5152 multiline: multiline,
5159 * Button config that displays a single OK button
5164 * Button config that displays Yes and No buttons
5167 YESNO : {yes:true, no:true},
5169 * Button config that displays OK and Cancel buttons
5172 OKCANCEL : {ok:true, cancel:true},
5174 * Button config that displays Yes, No and Cancel buttons
5177 YESNOCANCEL : {yes:true, no:true, cancel:true},
5180 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5183 defaultTextHeight : 75,
5185 * The maximum width in pixels of the message box (defaults to 600)
5190 * The minimum width in pixels of the message box (defaults to 100)
5195 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5196 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5199 minProgressWidth : 250,
5201 * An object containing the default button text strings that can be overriden for localized language support.
5202 * Supported properties are: ok, cancel, yes and no.
5203 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5216 * Shorthand for {@link Roo.MessageBox}
5218 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5219 Roo.Msg = Roo.Msg || Roo.MessageBox;
5228 * @class Roo.bootstrap.Navbar
5229 * @extends Roo.bootstrap.Component
5230 * Bootstrap Navbar class
5233 * Create a new Navbar
5234 * @param {Object} config The config object
5238 Roo.bootstrap.Navbar = function(config){
5239 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5243 * @event beforetoggle
5244 * Fire before toggle the menu
5245 * @param {Roo.EventObject} e
5247 "beforetoggle" : true
5251 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5260 getAutoCreate : function(){
5263 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5267 initEvents :function ()
5269 //Roo.log(this.el.select('.navbar-toggle',true));
5270 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5277 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5279 var size = this.el.getSize();
5280 this.maskEl.setSize(size.width, size.height);
5281 this.maskEl.enableDisplayMode("block");
5290 getChildContainer : function()
5292 if (this.el && this.el.select('.collapse').getCount()) {
5293 return this.el.select('.collapse',true).first();
5308 onToggle : function()
5311 if(this.fireEvent('beforetoggle', this) === false){
5314 var ce = this.el.select('.navbar-collapse',true).first();
5316 if (!ce.hasClass('show')) {
5326 * Expand the navbar pulldown
5328 expand : function ()
5331 var ce = this.el.select('.navbar-collapse',true).first();
5332 if (ce.hasClass('collapsing')) {
5335 ce.dom.style.height = '';
5337 ce.addClass('in'); // old...
5338 ce.removeClass('collapse');
5339 ce.addClass('show');
5340 var h = ce.getHeight();
5342 ce.removeClass('show');
5343 // at this point we should be able to see it..
5344 ce.addClass('collapsing');
5346 ce.setHeight(0); // resize it ...
5347 ce.on('transitionend', function() {
5348 //Roo.log('done transition');
5349 ce.removeClass('collapsing');
5350 ce.addClass('show');
5351 ce.removeClass('collapse');
5353 ce.dom.style.height = '';
5354 }, this, { single: true} );
5356 ce.dom.scrollTop = 0;
5359 * Collapse the navbar pulldown
5361 collapse : function()
5363 var ce = this.el.select('.navbar-collapse',true).first();
5365 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5366 // it's collapsed or collapsing..
5369 ce.removeClass('in'); // old...
5370 ce.setHeight(ce.getHeight());
5371 ce.removeClass('show');
5372 ce.addClass('collapsing');
5374 ce.on('transitionend', function() {
5375 ce.dom.style.height = '';
5376 ce.removeClass('collapsing');
5377 ce.addClass('collapse');
5378 }, this, { single: true} );
5398 * @class Roo.bootstrap.NavSimplebar
5399 * @extends Roo.bootstrap.Navbar
5400 * Bootstrap Sidebar class
5402 * @cfg {Boolean} inverse is inverted color
5404 * @cfg {String} type (nav | pills | tabs)
5405 * @cfg {Boolean} arrangement stacked | justified
5406 * @cfg {String} align (left | right) alignment
5408 * @cfg {Boolean} main (true|false) main nav bar? default false
5409 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5411 * @cfg {String} tag (header|footer|nav|div) default is nav
5413 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5417 * Create a new Sidebar
5418 * @param {Object} config The config object
5422 Roo.bootstrap.NavSimplebar = function(config){
5423 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5426 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5442 getAutoCreate : function(){
5446 tag : this.tag || 'div',
5447 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5449 if (['light','white'].indexOf(this.weight) > -1) {
5450 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5452 cfg.cls += ' bg-' + this.weight;
5455 cfg.cls += ' navbar-inverse';
5459 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5461 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5470 cls: 'nav nav-' + this.xtype,
5476 this.type = this.type || 'nav';
5477 if (['tabs','pills'].indexOf(this.type) != -1) {
5478 cfg.cn[0].cls += ' nav-' + this.type
5482 if (this.type!=='nav') {
5483 Roo.log('nav type must be nav/tabs/pills')
5485 cfg.cn[0].cls += ' navbar-nav'
5491 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5492 cfg.cn[0].cls += ' nav-' + this.arrangement;
5496 if (this.align === 'right') {
5497 cfg.cn[0].cls += ' navbar-right';
5522 * navbar-expand-md fixed-top
5526 * @class Roo.bootstrap.NavHeaderbar
5527 * @extends Roo.bootstrap.NavSimplebar
5528 * Bootstrap Sidebar class
5530 * @cfg {String} brand what is brand
5531 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5532 * @cfg {String} brand_href href of the brand
5533 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5534 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5535 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5536 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5539 * Create a new Sidebar
5540 * @param {Object} config The config object
5544 Roo.bootstrap.NavHeaderbar = function(config){
5545 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5549 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5556 desktopCenter : false,
5559 getAutoCreate : function(){
5562 tag: this.nav || 'nav',
5563 cls: 'navbar navbar-expand-md',
5569 if (this.desktopCenter) {
5570 cn.push({cls : 'container', cn : []});
5578 cls: 'navbar-toggle navbar-toggler',
5579 'data-toggle': 'collapse',
5584 html: 'Toggle navigation'
5588 cls: 'icon-bar navbar-toggler-icon'
5601 cn.push( Roo.bootstrap.version == 4 ? btn : {
5603 cls: 'navbar-header',
5612 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5616 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5618 if (['light','white'].indexOf(this.weight) > -1) {
5619 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5621 cfg.cls += ' bg-' + this.weight;
5624 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5625 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5627 // tag can override this..
5629 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5632 if (this.brand !== '') {
5633 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5634 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5636 href: this.brand_href ? this.brand_href : '#',
5637 cls: 'navbar-brand',
5645 cfg.cls += ' main-nav';
5653 getHeaderChildContainer : function()
5655 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5656 return this.el.select('.navbar-header',true).first();
5659 return this.getChildContainer();
5662 getChildContainer : function()
5665 return this.el.select('.roo-navbar-collapse',true).first();
5670 initEvents : function()
5672 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5674 if (this.autohide) {
5679 Roo.get(document).on('scroll',function(e) {
5680 var ns = Roo.get(document).getScroll().top;
5681 var os = prevScroll;
5685 ft.removeClass('slideDown');
5686 ft.addClass('slideUp');
5689 ft.removeClass('slideUp');
5690 ft.addClass('slideDown');
5711 * @class Roo.bootstrap.NavSidebar
5712 * @extends Roo.bootstrap.Navbar
5713 * Bootstrap Sidebar class
5716 * Create a new Sidebar
5717 * @param {Object} config The config object
5721 Roo.bootstrap.NavSidebar = function(config){
5722 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5725 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5727 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5729 getAutoCreate : function(){
5734 cls: 'sidebar sidebar-nav'
5756 * @class Roo.bootstrap.NavGroup
5757 * @extends Roo.bootstrap.Component
5758 * Bootstrap NavGroup class
5759 * @cfg {String} align (left|right)
5760 * @cfg {Boolean} inverse
5761 * @cfg {String} type (nav|pills|tab) default nav
5762 * @cfg {String} navId - reference Id for navbar.
5763 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5766 * Create a new nav group
5767 * @param {Object} config The config object
5770 Roo.bootstrap.NavGroup = function(config){
5771 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5774 Roo.bootstrap.NavGroup.register(this);
5778 * Fires when the active item changes
5779 * @param {Roo.bootstrap.NavGroup} this
5780 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5781 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5788 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5800 getAutoCreate : function()
5802 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5808 if (Roo.bootstrap.version == 4) {
5809 if (['tabs','pills'].indexOf(this.type) != -1) {
5810 cfg.cls += ' nav-' + this.type;
5812 // trying to remove so header bar can right align top?
5813 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5814 // do not use on header bar...
5815 cfg.cls += ' navbar-nav';
5820 if (['tabs','pills'].indexOf(this.type) != -1) {
5821 cfg.cls += ' nav-' + this.type
5823 if (this.type !== 'nav') {
5824 Roo.log('nav type must be nav/tabs/pills')
5826 cfg.cls += ' navbar-nav'
5830 if (this.parent() && this.parent().sidebar) {
5833 cls: 'dashboard-menu sidebar-menu'
5839 if (this.form === true) {
5842 cls: 'navbar-form form-inline'
5844 //nav navbar-right ml-md-auto
5845 if (this.align === 'right') {
5846 cfg.cls += ' navbar-right ml-md-auto';
5848 cfg.cls += ' navbar-left';
5852 if (this.align === 'right') {
5853 cfg.cls += ' navbar-right ml-md-auto';
5855 cfg.cls += ' mr-auto';
5859 cfg.cls += ' navbar-inverse';
5867 * sets the active Navigation item
5868 * @param {Roo.bootstrap.NavItem} the new current navitem
5870 setActiveItem : function(item)
5873 Roo.each(this.navItems, function(v){
5878 v.setActive(false, true);
5885 item.setActive(true, true);
5886 this.fireEvent('changed', this, item, prev);
5891 * gets the active Navigation item
5892 * @return {Roo.bootstrap.NavItem} the current navitem
5894 getActive : function()
5898 Roo.each(this.navItems, function(v){
5909 indexOfNav : function()
5913 Roo.each(this.navItems, function(v,i){
5924 * adds a Navigation item
5925 * @param {Roo.bootstrap.NavItem} the navitem to add
5927 addItem : function(cfg)
5929 if (this.form && Roo.bootstrap.version == 4) {
5932 var cn = new Roo.bootstrap.NavItem(cfg);
5934 cn.parentId = this.id;
5935 cn.onRender(this.el, null);
5939 * register a Navigation item
5940 * @param {Roo.bootstrap.NavItem} the navitem to add
5942 register : function(item)
5944 this.navItems.push( item);
5945 item.navId = this.navId;
5950 * clear all the Navigation item
5953 clearAll : function()
5956 this.el.dom.innerHTML = '';
5959 getNavItem: function(tabId)
5962 Roo.each(this.navItems, function(e) {
5963 if (e.tabId == tabId) {
5973 setActiveNext : function()
5975 var i = this.indexOfNav(this.getActive());
5976 if (i > this.navItems.length) {
5979 this.setActiveItem(this.navItems[i+1]);
5981 setActivePrev : function()
5983 var i = this.indexOfNav(this.getActive());
5987 this.setActiveItem(this.navItems[i-1]);
5989 clearWasActive : function(except) {
5990 Roo.each(this.navItems, function(e) {
5991 if (e.tabId != except.tabId && e.was_active) {
5992 e.was_active = false;
5999 getWasActive : function ()
6002 Roo.each(this.navItems, function(e) {
6017 Roo.apply(Roo.bootstrap.NavGroup, {
6021 * register a Navigation Group
6022 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6024 register : function(navgrp)
6026 this.groups[navgrp.navId] = navgrp;
6030 * fetch a Navigation Group based on the navigation ID
6031 * @param {string} the navgroup to add
6032 * @returns {Roo.bootstrap.NavGroup} the navgroup
6034 get: function(navId) {
6035 if (typeof(this.groups[navId]) == 'undefined') {
6037 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6039 return this.groups[navId] ;
6054 * @class Roo.bootstrap.NavItem
6055 * @extends Roo.bootstrap.Component
6056 * Bootstrap Navbar.NavItem class
6057 * @cfg {String} href link to
6058 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6059 * @cfg {Boolean} button_outline show and outlined button
6060 * @cfg {String} html content of button
6061 * @cfg {String} badge text inside badge
6062 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6063 * @cfg {String} glyphicon DEPRICATED - use fa
6064 * @cfg {String} icon DEPRICATED - use fa
6065 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6066 * @cfg {Boolean} active Is item active
6067 * @cfg {Boolean} disabled Is item disabled
6068 * @cfg {String} linkcls Link Class
6069 * @cfg {Boolean} preventDefault (true | false) default false
6070 * @cfg {String} tabId the tab that this item activates.
6071 * @cfg {String} tagtype (a|span) render as a href or span?
6072 * @cfg {Boolean} animateRef (true|false) link to element default false
6075 * Create a new Navbar Item
6076 * @param {Object} config The config object
6078 Roo.bootstrap.NavItem = function(config){
6079 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6084 * The raw click event for the entire grid.
6085 * @param {Roo.EventObject} e
6090 * Fires when the active item active state changes
6091 * @param {Roo.bootstrap.NavItem} this
6092 * @param {boolean} state the new state
6098 * Fires when scroll to element
6099 * @param {Roo.bootstrap.NavItem} this
6100 * @param {Object} options
6101 * @param {Roo.EventObject} e
6109 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6118 preventDefault : false,
6126 button_outline : false,
6130 getAutoCreate : function(){
6137 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6140 cfg.cls += ' active' ;
6142 if (this.disabled) {
6143 cfg.cls += ' disabled';
6147 if (this.button_weight.length) {
6148 cfg.tag = this.href ? 'a' : 'button';
6149 cfg.html = this.html || '';
6150 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6152 cfg.href = this.href;
6155 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6158 // menu .. should add dropdown-menu class - so no need for carat..
6160 if (this.badge !== '') {
6162 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6167 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6171 href : this.href || "#",
6172 html: this.html || ''
6175 if (this.tagtype == 'a') {
6176 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6180 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6183 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6185 if(this.glyphicon) {
6186 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6191 cfg.cn[0].html += " <span class='caret'></span>";
6195 if (this.badge !== '') {
6197 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6205 onRender : function(ct, position)
6207 // Roo.log("Call onRender: " + this.xtype);
6208 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6212 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6213 this.navLink = this.el.select('.nav-link',true).first();
6218 initEvents: function()
6220 if (typeof (this.menu) != 'undefined') {
6221 this.menu.parentType = this.xtype;
6222 this.menu.triggerEl = this.el;
6223 this.menu = this.addxtype(Roo.apply({}, this.menu));
6226 this.el.on('click', this.onClick, this);
6228 //if(this.tagtype == 'span'){
6229 // this.el.select('span',true).on('click', this.onClick, this);
6232 // at this point parent should be available..
6233 this.parent().register(this);
6236 onClick : function(e)
6238 if (e.getTarget('.dropdown-menu-item')) {
6239 // did you click on a menu itemm.... - then don't trigger onclick..
6244 this.preventDefault ||
6247 Roo.log("NavItem - prevent Default?");
6251 if (this.disabled) {
6255 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6256 if (tg && tg.transition) {
6257 Roo.log("waiting for the transitionend");
6263 //Roo.log("fire event clicked");
6264 if(this.fireEvent('click', this, e) === false){
6268 if(this.tagtype == 'span'){
6272 //Roo.log(this.href);
6273 var ael = this.el.select('a',true).first();
6276 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6277 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6278 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6279 return; // ignore... - it's a 'hash' to another page.
6281 Roo.log("NavItem - prevent Default?");
6283 this.scrollToElement(e);
6287 var p = this.parent();
6289 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6290 if (typeof(p.setActiveItem) !== 'undefined') {
6291 p.setActiveItem(this);
6295 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6296 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6297 // remove the collapsed menu expand...
6298 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6302 isActive: function () {
6305 setActive : function(state, fire, is_was_active)
6307 if (this.active && !state && this.navId) {
6308 this.was_active = true;
6309 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6311 nv.clearWasActive(this);
6315 this.active = state;
6318 this.el.removeClass('active');
6319 this.navLink ? this.navLink.removeClass('active') : false;
6320 } else if (!this.el.hasClass('active')) {
6322 this.el.addClass('active');
6323 if (Roo.bootstrap.version == 4 && this.navLink ) {
6324 this.navLink.addClass('active');
6329 this.fireEvent('changed', this, state);
6332 // show a panel if it's registered and related..
6334 if (!this.navId || !this.tabId || !state || is_was_active) {
6338 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6342 var pan = tg.getPanelByName(this.tabId);
6346 // if we can not flip to new panel - go back to old nav highlight..
6347 if (false == tg.showPanel(pan)) {
6348 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6350 var onav = nv.getWasActive();
6352 onav.setActive(true, false, true);
6361 // this should not be here...
6362 setDisabled : function(state)
6364 this.disabled = state;
6366 this.el.removeClass('disabled');
6367 } else if (!this.el.hasClass('disabled')) {
6368 this.el.addClass('disabled');
6374 * Fetch the element to display the tooltip on.
6375 * @return {Roo.Element} defaults to this.el
6377 tooltipEl : function()
6379 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6382 scrollToElement : function(e)
6384 var c = document.body;
6387 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6389 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6390 c = document.documentElement;
6393 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6399 var o = target.calcOffsetsTo(c);
6406 this.fireEvent('scrollto', this, options, e);
6408 Roo.get(c).scrollTo('top', options.value, true);
6421 * <span> icon </span>
6422 * <span> text </span>
6423 * <span>badge </span>
6427 * @class Roo.bootstrap.NavSidebarItem
6428 * @extends Roo.bootstrap.NavItem
6429 * Bootstrap Navbar.NavSidebarItem class
6430 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6431 * {Boolean} open is the menu open
6432 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6433 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6434 * {String} buttonSize (sm|md|lg)the extra classes for the button
6435 * {Boolean} showArrow show arrow next to the text (default true)
6437 * Create a new Navbar Button
6438 * @param {Object} config The config object
6440 Roo.bootstrap.NavSidebarItem = function(config){
6441 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6446 * The raw click event for the entire grid.
6447 * @param {Roo.EventObject} e
6452 * Fires when the active item active state changes
6453 * @param {Roo.bootstrap.NavSidebarItem} this
6454 * @param {boolean} state the new state
6462 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6464 badgeWeight : 'default',
6470 buttonWeight : 'default',
6476 getAutoCreate : function(){
6481 href : this.href || '#',
6487 if(this.buttonView){
6490 href : this.href || '#',
6491 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6504 cfg.cls += ' active';
6507 if (this.disabled) {
6508 cfg.cls += ' disabled';
6511 cfg.cls += ' open x-open';
6514 if (this.glyphicon || this.icon) {
6515 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6516 a.cn.push({ tag : 'i', cls : c }) ;
6519 if(!this.buttonView){
6522 html : this.html || ''
6529 if (this.badge !== '') {
6530 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6536 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6539 a.cls += ' dropdown-toggle treeview' ;
6545 initEvents : function()
6547 if (typeof (this.menu) != 'undefined') {
6548 this.menu.parentType = this.xtype;
6549 this.menu.triggerEl = this.el;
6550 this.menu = this.addxtype(Roo.apply({}, this.menu));
6553 this.el.on('click', this.onClick, this);
6555 if(this.badge !== ''){
6556 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6561 onClick : function(e)
6568 if(this.preventDefault){
6572 this.fireEvent('click', this, e);
6575 disable : function()
6577 this.setDisabled(true);
6582 this.setDisabled(false);
6585 setDisabled : function(state)
6587 if(this.disabled == state){
6591 this.disabled = state;
6594 this.el.addClass('disabled');
6598 this.el.removeClass('disabled');
6603 setActive : function(state)
6605 if(this.active == state){
6609 this.active = state;
6612 this.el.addClass('active');
6616 this.el.removeClass('active');
6621 isActive: function ()
6626 setBadge : function(str)
6632 this.badgeEl.dom.innerHTML = str;
6647 Roo.namespace('Roo.bootstrap.breadcrumb');
6651 * @class Roo.bootstrap.breadcrumb.Nav
6652 * @extends Roo.bootstrap.Component
6653 * Bootstrap Breadcrumb Nav Class
6655 * @children Roo.bootstrap.breadcrumb.Item
6658 * Create a new breadcrumb.Nav
6659 * @param {Object} config The config object
6663 Roo.bootstrap.breadcrumb.Nav = function(config){
6664 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6669 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6671 getAutoCreate : function()
6688 initEvents: function()
6690 this.olEl = this.el.select('ol',true).first();
6692 getChildContainer : function()
6708 * @class Roo.bootstrap.breadcrumb.Nav
6709 * @extends Roo.bootstrap.Component
6710 * Bootstrap Breadcrumb Nav Class
6712 * @children Roo.bootstrap.breadcrumb.Component
6713 * @cfg {String} html the content of the link.
6714 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6715 * @cfg {Boolean} active is it active
6719 * Create a new breadcrumb.Nav
6720 * @param {Object} config The config object
6723 Roo.bootstrap.breadcrumb.Item = function(config){
6724 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6729 * The img click event for the img.
6730 * @param {Roo.EventObject} e
6737 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6742 getAutoCreate : function()
6747 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6749 if (this.href !== false) {
6756 cfg.html = this.html;
6762 initEvents: function()
6765 this.el.select('a', true).first().on('click',this.onClick, this)
6769 onClick : function(e)
6772 this.fireEvent('click',this, e);
6785 * @class Roo.bootstrap.Row
6786 * @extends Roo.bootstrap.Component
6787 * Bootstrap Row class (contains columns...)
6791 * @param {Object} config The config object
6794 Roo.bootstrap.Row = function(config){
6795 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6798 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6800 getAutoCreate : function(){
6819 * @class Roo.bootstrap.Pagination
6820 * @extends Roo.bootstrap.Component
6821 * Bootstrap Pagination class
6822 * @cfg {String} size xs | sm | md | lg
6823 * @cfg {Boolean} inverse false | true
6826 * Create a new Pagination
6827 * @param {Object} config The config object
6830 Roo.bootstrap.Pagination = function(config){
6831 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6834 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6840 getAutoCreate : function(){
6846 cfg.cls += ' inverse';
6852 cfg.cls += " " + this.cls;
6870 * @class Roo.bootstrap.PaginationItem
6871 * @extends Roo.bootstrap.Component
6872 * Bootstrap PaginationItem class
6873 * @cfg {String} html text
6874 * @cfg {String} href the link
6875 * @cfg {Boolean} preventDefault (true | false) default true
6876 * @cfg {Boolean} active (true | false) default false
6877 * @cfg {Boolean} disabled default false
6881 * Create a new PaginationItem
6882 * @param {Object} config The config object
6886 Roo.bootstrap.PaginationItem = function(config){
6887 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6892 * The raw click event for the entire grid.
6893 * @param {Roo.EventObject} e
6899 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6903 preventDefault: true,
6908 getAutoCreate : function(){
6914 href : this.href ? this.href : '#',
6915 html : this.html ? this.html : ''
6925 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6929 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6935 initEvents: function() {
6937 this.el.on('click', this.onClick, this);
6940 onClick : function(e)
6942 Roo.log('PaginationItem on click ');
6943 if(this.preventDefault){
6951 this.fireEvent('click', this, e);
6967 * @class Roo.bootstrap.Slider
6968 * @extends Roo.bootstrap.Component
6969 * Bootstrap Slider class
6972 * Create a new Slider
6973 * @param {Object} config The config object
6976 Roo.bootstrap.Slider = function(config){
6977 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6980 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
6982 getAutoCreate : function(){
6986 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6990 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7002 * Ext JS Library 1.1.1
7003 * Copyright(c) 2006-2007, Ext JS, LLC.
7005 * Originally Released Under LGPL - original licence link has changed is not relivant.
7008 * <script type="text/javascript">
7013 * @class Roo.grid.ColumnModel
7014 * @extends Roo.util.Observable
7015 * This is the default implementation of a ColumnModel used by the Grid. It defines
7016 * the columns in the grid.
7019 var colModel = new Roo.grid.ColumnModel([
7020 {header: "Ticker", width: 60, sortable: true, locked: true},
7021 {header: "Company Name", width: 150, sortable: true},
7022 {header: "Market Cap.", width: 100, sortable: true},
7023 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7024 {header: "Employees", width: 100, sortable: true, resizable: false}
7029 * The config options listed for this class are options which may appear in each
7030 * individual column definition.
7031 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7033 * @param {Object} config An Array of column config objects. See this class's
7034 * config objects for details.
7036 Roo.grid.ColumnModel = function(config){
7038 * The config passed into the constructor
7040 this.config = config;
7043 // if no id, create one
7044 // if the column does not have a dataIndex mapping,
7045 // map it to the order it is in the config
7046 for(var i = 0, len = config.length; i < len; i++){
7048 if(typeof c.dataIndex == "undefined"){
7051 if(typeof c.renderer == "string"){
7052 c.renderer = Roo.util.Format[c.renderer];
7054 if(typeof c.id == "undefined"){
7057 if(c.editor && c.editor.xtype){
7058 c.editor = Roo.factory(c.editor, Roo.grid);
7060 if(c.editor && c.editor.isFormField){
7061 c.editor = new Roo.grid.GridEditor(c.editor);
7063 this.lookup[c.id] = c;
7067 * The width of columns which have no width specified (defaults to 100)
7070 this.defaultWidth = 100;
7073 * Default sortable of columns which have no sortable specified (defaults to false)
7076 this.defaultSortable = false;
7080 * @event widthchange
7081 * Fires when the width of a column changes.
7082 * @param {ColumnModel} this
7083 * @param {Number} columnIndex The column index
7084 * @param {Number} newWidth The new width
7086 "widthchange": true,
7088 * @event headerchange
7089 * Fires when the text of a header changes.
7090 * @param {ColumnModel} this
7091 * @param {Number} columnIndex The column index
7092 * @param {Number} newText The new header text
7094 "headerchange": true,
7096 * @event hiddenchange
7097 * Fires when a column is hidden or "unhidden".
7098 * @param {ColumnModel} this
7099 * @param {Number} columnIndex The column index
7100 * @param {Boolean} hidden true if hidden, false otherwise
7102 "hiddenchange": true,
7104 * @event columnmoved
7105 * Fires when a column is moved.
7106 * @param {ColumnModel} this
7107 * @param {Number} oldIndex
7108 * @param {Number} newIndex
7110 "columnmoved" : true,
7112 * @event columlockchange
7113 * Fires when a column's locked state is changed
7114 * @param {ColumnModel} this
7115 * @param {Number} colIndex
7116 * @param {Boolean} locked true if locked
7118 "columnlockchange" : true
7120 Roo.grid.ColumnModel.superclass.constructor.call(this);
7122 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7124 * @cfg {String} header The header text to display in the Grid view.
7127 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7128 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7129 * specified, the column's index is used as an index into the Record's data Array.
7132 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7133 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7136 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7137 * Defaults to the value of the {@link #defaultSortable} property.
7138 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7141 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7144 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7147 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7150 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7153 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7154 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7155 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7156 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7159 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7162 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7165 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7168 * @cfg {String} cursor (Optional)
7171 * @cfg {String} tooltip (Optional)
7174 * @cfg {Number} xs (Optional)
7177 * @cfg {Number} sm (Optional)
7180 * @cfg {Number} md (Optional)
7183 * @cfg {Number} lg (Optional)
7186 * Returns the id of the column at the specified index.
7187 * @param {Number} index The column index
7188 * @return {String} the id
7190 getColumnId : function(index){
7191 return this.config[index].id;
7195 * Returns the column for a specified id.
7196 * @param {String} id The column id
7197 * @return {Object} the column
7199 getColumnById : function(id){
7200 return this.lookup[id];
7205 * Returns the column for a specified dataIndex.
7206 * @param {String} dataIndex The column dataIndex
7207 * @return {Object|Boolean} the column or false if not found
7209 getColumnByDataIndex: function(dataIndex){
7210 var index = this.findColumnIndex(dataIndex);
7211 return index > -1 ? this.config[index] : false;
7215 * Returns the index for a specified column id.
7216 * @param {String} id The column id
7217 * @return {Number} the index, or -1 if not found
7219 getIndexById : function(id){
7220 for(var i = 0, len = this.config.length; i < len; i++){
7221 if(this.config[i].id == id){
7229 * Returns the index for a specified column dataIndex.
7230 * @param {String} dataIndex The column dataIndex
7231 * @return {Number} the index, or -1 if not found
7234 findColumnIndex : function(dataIndex){
7235 for(var i = 0, len = this.config.length; i < len; i++){
7236 if(this.config[i].dataIndex == dataIndex){
7244 moveColumn : function(oldIndex, newIndex){
7245 var c = this.config[oldIndex];
7246 this.config.splice(oldIndex, 1);
7247 this.config.splice(newIndex, 0, c);
7248 this.dataMap = null;
7249 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7252 isLocked : function(colIndex){
7253 return this.config[colIndex].locked === true;
7256 setLocked : function(colIndex, value, suppressEvent){
7257 if(this.isLocked(colIndex) == value){
7260 this.config[colIndex].locked = value;
7262 this.fireEvent("columnlockchange", this, colIndex, value);
7266 getTotalLockedWidth : function(){
7268 for(var i = 0; i < this.config.length; i++){
7269 if(this.isLocked(i) && !this.isHidden(i)){
7270 this.totalWidth += this.getColumnWidth(i);
7276 getLockedCount : function(){
7277 for(var i = 0, len = this.config.length; i < len; i++){
7278 if(!this.isLocked(i)){
7283 return this.config.length;
7287 * Returns the number of columns.
7290 getColumnCount : function(visibleOnly){
7291 if(visibleOnly === true){
7293 for(var i = 0, len = this.config.length; i < len; i++){
7294 if(!this.isHidden(i)){
7300 return this.config.length;
7304 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7305 * @param {Function} fn
7306 * @param {Object} scope (optional)
7307 * @return {Array} result
7309 getColumnsBy : function(fn, scope){
7311 for(var i = 0, len = this.config.length; i < len; i++){
7312 var c = this.config[i];
7313 if(fn.call(scope||this, c, i) === true){
7321 * Returns true if the specified column is sortable.
7322 * @param {Number} col The column index
7325 isSortable : function(col){
7326 if(typeof this.config[col].sortable == "undefined"){
7327 return this.defaultSortable;
7329 return this.config[col].sortable;
7333 * Returns the rendering (formatting) function defined for the column.
7334 * @param {Number} col The column index.
7335 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7337 getRenderer : function(col){
7338 if(!this.config[col].renderer){
7339 return Roo.grid.ColumnModel.defaultRenderer;
7341 return this.config[col].renderer;
7345 * Sets the rendering (formatting) function for a column.
7346 * @param {Number} col The column index
7347 * @param {Function} fn The function to use to process the cell's raw data
7348 * to return HTML markup for the grid view. The render function is called with
7349 * the following parameters:<ul>
7350 * <li>Data value.</li>
7351 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7352 * <li>css A CSS style string to apply to the table cell.</li>
7353 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7354 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7355 * <li>Row index</li>
7356 * <li>Column index</li>
7357 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7359 setRenderer : function(col, fn){
7360 this.config[col].renderer = fn;
7364 * Returns the width for the specified column.
7365 * @param {Number} col The column index
7368 getColumnWidth : function(col){
7369 return this.config[col].width * 1 || this.defaultWidth;
7373 * Sets the width for a column.
7374 * @param {Number} col The column index
7375 * @param {Number} width The new width
7377 setColumnWidth : function(col, width, suppressEvent){
7378 this.config[col].width = width;
7379 this.totalWidth = null;
7381 this.fireEvent("widthchange", this, col, width);
7386 * Returns the total width of all columns.
7387 * @param {Boolean} includeHidden True to include hidden column widths
7390 getTotalWidth : function(includeHidden){
7391 if(!this.totalWidth){
7392 this.totalWidth = 0;
7393 for(var i = 0, len = this.config.length; i < len; i++){
7394 if(includeHidden || !this.isHidden(i)){
7395 this.totalWidth += this.getColumnWidth(i);
7399 return this.totalWidth;
7403 * Returns the header for the specified column.
7404 * @param {Number} col The column index
7407 getColumnHeader : function(col){
7408 return this.config[col].header;
7412 * Sets the header for a column.
7413 * @param {Number} col The column index
7414 * @param {String} header The new header
7416 setColumnHeader : function(col, header){
7417 this.config[col].header = header;
7418 this.fireEvent("headerchange", this, col, header);
7422 * Returns the tooltip for the specified column.
7423 * @param {Number} col The column index
7426 getColumnTooltip : function(col){
7427 return this.config[col].tooltip;
7430 * Sets the tooltip for a column.
7431 * @param {Number} col The column index
7432 * @param {String} tooltip The new tooltip
7434 setColumnTooltip : function(col, tooltip){
7435 this.config[col].tooltip = tooltip;
7439 * Returns the dataIndex for the specified column.
7440 * @param {Number} col The column index
7443 getDataIndex : function(col){
7444 return this.config[col].dataIndex;
7448 * Sets the dataIndex for a column.
7449 * @param {Number} col The column index
7450 * @param {Number} dataIndex The new dataIndex
7452 setDataIndex : function(col, dataIndex){
7453 this.config[col].dataIndex = dataIndex;
7459 * Returns true if the cell is editable.
7460 * @param {Number} colIndex The column index
7461 * @param {Number} rowIndex The row index - this is nto actually used..?
7464 isCellEditable : function(colIndex, rowIndex){
7465 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7469 * Returns the editor defined for the cell/column.
7470 * return false or null to disable editing.
7471 * @param {Number} colIndex The column index
7472 * @param {Number} rowIndex The row index
7475 getCellEditor : function(colIndex, rowIndex){
7476 return this.config[colIndex].editor;
7480 * Sets if a column is editable.
7481 * @param {Number} col The column index
7482 * @param {Boolean} editable True if the column is editable
7484 setEditable : function(col, editable){
7485 this.config[col].editable = editable;
7490 * Returns true if the column is hidden.
7491 * @param {Number} colIndex The column index
7494 isHidden : function(colIndex){
7495 return this.config[colIndex].hidden;
7500 * Returns true if the column width cannot be changed
7502 isFixed : function(colIndex){
7503 return this.config[colIndex].fixed;
7507 * Returns true if the column can be resized
7510 isResizable : function(colIndex){
7511 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7514 * Sets if a column is hidden.
7515 * @param {Number} colIndex The column index
7516 * @param {Boolean} hidden True if the column is hidden
7518 setHidden : function(colIndex, hidden){
7519 this.config[colIndex].hidden = hidden;
7520 this.totalWidth = null;
7521 this.fireEvent("hiddenchange", this, colIndex, hidden);
7525 * Sets the editor for a column.
7526 * @param {Number} col The column index
7527 * @param {Object} editor The editor object
7529 setEditor : function(col, editor){
7530 this.config[col].editor = editor;
7534 Roo.grid.ColumnModel.defaultRenderer = function(value)
7536 if(typeof value == "object") {
7539 if(typeof value == "string" && value.length < 1){
7543 return String.format("{0}", value);
7546 // Alias for backwards compatibility
7547 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7550 * Ext JS Library 1.1.1
7551 * Copyright(c) 2006-2007, Ext JS, LLC.
7553 * Originally Released Under LGPL - original licence link has changed is not relivant.
7556 * <script type="text/javascript">
7560 * @class Roo.LoadMask
7561 * A simple utility class for generically masking elements while loading data. If the element being masked has
7562 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7563 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7564 * element's UpdateManager load indicator and will be destroyed after the initial load.
7566 * Create a new LoadMask
7567 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7568 * @param {Object} config The config object
7570 Roo.LoadMask = function(el, config){
7571 this.el = Roo.get(el);
7572 Roo.apply(this, config);
7574 this.store.on('beforeload', this.onBeforeLoad, this);
7575 this.store.on('load', this.onLoad, this);
7576 this.store.on('loadexception', this.onLoadException, this);
7577 this.removeMask = false;
7579 var um = this.el.getUpdateManager();
7580 um.showLoadIndicator = false; // disable the default indicator
7581 um.on('beforeupdate', this.onBeforeLoad, this);
7582 um.on('update', this.onLoad, this);
7583 um.on('failure', this.onLoad, this);
7584 this.removeMask = true;
7588 Roo.LoadMask.prototype = {
7590 * @cfg {Boolean} removeMask
7591 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7592 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7596 * The text to display in a centered loading message box (defaults to 'Loading...')
7600 * @cfg {String} msgCls
7601 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7603 msgCls : 'x-mask-loading',
7606 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7612 * Disables the mask to prevent it from being displayed
7614 disable : function(){
7615 this.disabled = true;
7619 * Enables the mask so that it can be displayed
7621 enable : function(){
7622 this.disabled = false;
7625 onLoadException : function()
7629 if (typeof(arguments[3]) != 'undefined') {
7630 Roo.MessageBox.alert("Error loading",arguments[3]);
7634 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7635 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7642 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7647 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7651 onBeforeLoad : function(){
7653 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7658 destroy : function(){
7660 this.store.un('beforeload', this.onBeforeLoad, this);
7661 this.store.un('load', this.onLoad, this);
7662 this.store.un('loadexception', this.onLoadException, this);
7664 var um = this.el.getUpdateManager();
7665 um.un('beforeupdate', this.onBeforeLoad, this);
7666 um.un('update', this.onLoad, this);
7667 um.un('failure', this.onLoad, this);
7678 * @class Roo.bootstrap.Table
7679 * @extends Roo.bootstrap.Component
7680 * Bootstrap Table class
7681 * @cfg {String} cls table class
7682 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7683 * @cfg {String} bgcolor Specifies the background color for a table
7684 * @cfg {Number} border Specifies whether the table cells should have borders or not
7685 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7686 * @cfg {Number} cellspacing Specifies the space between cells
7687 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7688 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7689 * @cfg {String} sortable Specifies that the table should be sortable
7690 * @cfg {String} summary Specifies a summary of the content of a table
7691 * @cfg {Number} width Specifies the width of a table
7692 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7694 * @cfg {boolean} striped Should the rows be alternative striped
7695 * @cfg {boolean} bordered Add borders to the table
7696 * @cfg {boolean} hover Add hover highlighting
7697 * @cfg {boolean} condensed Format condensed
7698 * @cfg {boolean} responsive Format condensed
7699 * @cfg {Boolean} loadMask (true|false) default false
7700 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7701 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7702 * @cfg {Boolean} rowSelection (true|false) default false
7703 * @cfg {Boolean} cellSelection (true|false) default false
7704 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7705 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7706 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7707 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7711 * Create a new Table
7712 * @param {Object} config The config object
7715 Roo.bootstrap.Table = function(config){
7716 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7721 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7722 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7723 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7724 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7726 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7728 this.sm.grid = this;
7729 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7730 this.sm = this.selModel;
7731 this.sm.xmodule = this.xmodule || false;
7734 if (this.cm && typeof(this.cm.config) == 'undefined') {
7735 this.colModel = new Roo.grid.ColumnModel(this.cm);
7736 this.cm = this.colModel;
7737 this.cm.xmodule = this.xmodule || false;
7740 this.store= Roo.factory(this.store, Roo.data);
7741 this.ds = this.store;
7742 this.ds.xmodule = this.xmodule || false;
7745 if (this.footer && this.store) {
7746 this.footer.dataSource = this.ds;
7747 this.footer = Roo.factory(this.footer);
7754 * Fires when a cell is clicked
7755 * @param {Roo.bootstrap.Table} this
7756 * @param {Roo.Element} el
7757 * @param {Number} rowIndex
7758 * @param {Number} columnIndex
7759 * @param {Roo.EventObject} e
7763 * @event celldblclick
7764 * Fires when a cell is double clicked
7765 * @param {Roo.bootstrap.Table} this
7766 * @param {Roo.Element} el
7767 * @param {Number} rowIndex
7768 * @param {Number} columnIndex
7769 * @param {Roo.EventObject} e
7771 "celldblclick" : true,
7774 * Fires when a row is clicked
7775 * @param {Roo.bootstrap.Table} this
7776 * @param {Roo.Element} el
7777 * @param {Number} rowIndex
7778 * @param {Roo.EventObject} e
7782 * @event rowdblclick
7783 * Fires when a row is double clicked
7784 * @param {Roo.bootstrap.Table} this
7785 * @param {Roo.Element} el
7786 * @param {Number} rowIndex
7787 * @param {Roo.EventObject} e
7789 "rowdblclick" : true,
7792 * Fires when a mouseover occur
7793 * @param {Roo.bootstrap.Table} this
7794 * @param {Roo.Element} el
7795 * @param {Number} rowIndex
7796 * @param {Number} columnIndex
7797 * @param {Roo.EventObject} e
7802 * Fires when a mouseout occur
7803 * @param {Roo.bootstrap.Table} this
7804 * @param {Roo.Element} el
7805 * @param {Number} rowIndex
7806 * @param {Number} columnIndex
7807 * @param {Roo.EventObject} e
7812 * Fires when a row is rendered, so you can change add a style to it.
7813 * @param {Roo.bootstrap.Table} this
7814 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7818 * @event rowsrendered
7819 * Fires when all the rows have been rendered
7820 * @param {Roo.bootstrap.Table} this
7822 'rowsrendered' : true,
7824 * @event contextmenu
7825 * The raw contextmenu event for the entire grid.
7826 * @param {Roo.EventObject} e
7828 "contextmenu" : true,
7830 * @event rowcontextmenu
7831 * Fires when a row is right clicked
7832 * @param {Roo.bootstrap.Table} this
7833 * @param {Number} rowIndex
7834 * @param {Roo.EventObject} e
7836 "rowcontextmenu" : true,
7838 * @event cellcontextmenu
7839 * Fires when a cell is right clicked
7840 * @param {Roo.bootstrap.Table} this
7841 * @param {Number} rowIndex
7842 * @param {Number} cellIndex
7843 * @param {Roo.EventObject} e
7845 "cellcontextmenu" : true,
7847 * @event headercontextmenu
7848 * Fires when a header is right clicked
7849 * @param {Roo.bootstrap.Table} this
7850 * @param {Number} columnIndex
7851 * @param {Roo.EventObject} e
7853 "headercontextmenu" : true
7857 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7883 rowSelection : false,
7884 cellSelection : false,
7887 // Roo.Element - the tbody
7889 // Roo.Element - thead element
7892 container: false, // used by gridpanel...
7898 auto_hide_footer : false,
7900 getAutoCreate : function()
7902 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7909 if (this.scrollBody) {
7910 cfg.cls += ' table-body-fixed';
7913 cfg.cls += ' table-striped';
7917 cfg.cls += ' table-hover';
7919 if (this.bordered) {
7920 cfg.cls += ' table-bordered';
7922 if (this.condensed) {
7923 cfg.cls += ' table-condensed';
7925 if (this.responsive) {
7926 cfg.cls += ' table-responsive';
7930 cfg.cls+= ' ' +this.cls;
7933 // this lot should be simplifed...
7946 ].forEach(function(k) {
7954 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7957 if(this.store || this.cm){
7958 if(this.headerShow){
7959 cfg.cn.push(this.renderHeader());
7962 cfg.cn.push(this.renderBody());
7964 if(this.footerShow){
7965 cfg.cn.push(this.renderFooter());
7967 // where does this come from?
7968 //cfg.cls+= ' TableGrid';
7971 return { cn : [ cfg ] };
7974 initEvents : function()
7976 if(!this.store || !this.cm){
7979 if (this.selModel) {
7980 this.selModel.initEvents();
7984 //Roo.log('initEvents with ds!!!!');
7986 this.mainBody = this.el.select('tbody', true).first();
7987 this.mainHead = this.el.select('thead', true).first();
7988 this.mainFoot = this.el.select('tfoot', true).first();
7994 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7995 e.on('click', _this.sort, _this);
7998 this.mainBody.on("click", this.onClick, this);
7999 this.mainBody.on("dblclick", this.onDblClick, this);
8001 // why is this done????? = it breaks dialogs??
8002 //this.parent().el.setStyle('position', 'relative');
8006 this.footer.parentId = this.id;
8007 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8010 this.el.select('tfoot tr td').first().addClass('hide');
8015 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8018 this.store.on('load', this.onLoad, this);
8019 this.store.on('beforeload', this.onBeforeLoad, this);
8020 this.store.on('update', this.onUpdate, this);
8021 this.store.on('add', this.onAdd, this);
8022 this.store.on("clear", this.clear, this);
8024 this.el.on("contextmenu", this.onContextMenu, this);
8026 this.mainBody.on('scroll', this.onBodyScroll, this);
8028 this.cm.on("headerchange", this.onHeaderChange, this);
8030 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8034 onContextMenu : function(e, t)
8036 this.processEvent("contextmenu", e);
8039 processEvent : function(name, e)
8041 if (name != 'touchstart' ) {
8042 this.fireEvent(name, e);
8045 var t = e.getTarget();
8047 var cell = Roo.get(t);
8053 if(cell.findParent('tfoot', false, true)){
8057 if(cell.findParent('thead', false, true)){
8059 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8060 cell = Roo.get(t).findParent('th', false, true);
8062 Roo.log("failed to find th in thead?");
8063 Roo.log(e.getTarget());
8068 var cellIndex = cell.dom.cellIndex;
8070 var ename = name == 'touchstart' ? 'click' : name;
8071 this.fireEvent("header" + ename, this, cellIndex, e);
8076 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8077 cell = Roo.get(t).findParent('td', false, true);
8079 Roo.log("failed to find th in tbody?");
8080 Roo.log(e.getTarget());
8085 var row = cell.findParent('tr', false, true);
8086 var cellIndex = cell.dom.cellIndex;
8087 var rowIndex = row.dom.rowIndex - 1;
8091 this.fireEvent("row" + name, this, rowIndex, e);
8095 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8101 onMouseover : function(e, el)
8103 var cell = Roo.get(el);
8109 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8110 cell = cell.findParent('td', false, true);
8113 var row = cell.findParent('tr', false, true);
8114 var cellIndex = cell.dom.cellIndex;
8115 var rowIndex = row.dom.rowIndex - 1; // start from 0
8117 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8121 onMouseout : function(e, el)
8123 var cell = Roo.get(el);
8129 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8130 cell = cell.findParent('td', false, true);
8133 var row = cell.findParent('tr', false, true);
8134 var cellIndex = cell.dom.cellIndex;
8135 var rowIndex = row.dom.rowIndex - 1; // start from 0
8137 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8141 onClick : function(e, el)
8143 var cell = Roo.get(el);
8145 if(!cell || (!this.cellSelection && !this.rowSelection)){
8149 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8150 cell = cell.findParent('td', false, true);
8153 if(!cell || typeof(cell) == 'undefined'){
8157 var row = cell.findParent('tr', false, true);
8159 if(!row || typeof(row) == 'undefined'){
8163 var cellIndex = cell.dom.cellIndex;
8164 var rowIndex = this.getRowIndex(row);
8166 // why??? - should these not be based on SelectionModel?
8167 if(this.cellSelection){
8168 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8171 if(this.rowSelection){
8172 this.fireEvent('rowclick', this, row, rowIndex, e);
8178 onDblClick : function(e,el)
8180 var cell = Roo.get(el);
8182 if(!cell || (!this.cellSelection && !this.rowSelection)){
8186 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8187 cell = cell.findParent('td', false, true);
8190 if(!cell || typeof(cell) == 'undefined'){
8194 var row = cell.findParent('tr', false, true);
8196 if(!row || typeof(row) == 'undefined'){
8200 var cellIndex = cell.dom.cellIndex;
8201 var rowIndex = this.getRowIndex(row);
8203 if(this.cellSelection){
8204 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8207 if(this.rowSelection){
8208 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8212 sort : function(e,el)
8214 var col = Roo.get(el);
8216 if(!col.hasClass('sortable')){
8220 var sort = col.attr('sort');
8223 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8227 this.store.sortInfo = {field : sort, direction : dir};
8230 Roo.log("calling footer first");
8231 this.footer.onClick('first');
8234 this.store.load({ params : { start : 0 } });
8238 renderHeader : function()
8246 this.totalWidth = 0;
8248 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8250 var config = cm.config[i];
8254 cls : 'x-hcol-' + i,
8256 html: cm.getColumnHeader(i)
8261 if(typeof(config.sortable) != 'undefined' && config.sortable){
8263 c.html = '<i class="glyphicon"></i>' + c.html;
8266 // could use BS4 hidden-..-down
8268 if(typeof(config.lgHeader) != 'undefined'){
8269 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8272 if(typeof(config.mdHeader) != 'undefined'){
8273 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8276 if(typeof(config.smHeader) != 'undefined'){
8277 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8280 if(typeof(config.xsHeader) != 'undefined'){
8281 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8288 if(typeof(config.tooltip) != 'undefined'){
8289 c.tooltip = config.tooltip;
8292 if(typeof(config.colspan) != 'undefined'){
8293 c.colspan = config.colspan;
8296 if(typeof(config.hidden) != 'undefined' && config.hidden){
8297 c.style += ' display:none;';
8300 if(typeof(config.dataIndex) != 'undefined'){
8301 c.sort = config.dataIndex;
8306 if(typeof(config.align) != 'undefined' && config.align.length){
8307 c.style += ' text-align:' + config.align + ';';
8310 if(typeof(config.width) != 'undefined'){
8311 c.style += ' width:' + config.width + 'px;';
8312 this.totalWidth += config.width;
8314 this.totalWidth += 100; // assume minimum of 100 per column?
8317 if(typeof(config.cls) != 'undefined'){
8318 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8321 ['xs','sm','md','lg'].map(function(size){
8323 if(typeof(config[size]) == 'undefined'){
8327 if (!config[size]) { // 0 = hidden
8328 // BS 4 '0' is treated as hide that column and below.
8329 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8333 c.cls += ' col-' + size + '-' + config[size] + (
8334 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8346 renderBody : function()
8356 colspan : this.cm.getColumnCount()
8366 renderFooter : function()
8376 colspan : this.cm.getColumnCount()
8390 // Roo.log('ds onload');
8395 var ds = this.store;
8397 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8398 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8399 if (_this.store.sortInfo) {
8401 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8402 e.select('i', true).addClass(['glyphicon-arrow-up']);
8405 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8406 e.select('i', true).addClass(['glyphicon-arrow-down']);
8411 var tbody = this.mainBody;
8413 if(ds.getCount() > 0){
8414 ds.data.each(function(d,rowIndex){
8415 var row = this.renderRow(cm, ds, rowIndex);
8417 tbody.createChild(row);
8421 if(row.cellObjects.length){
8422 Roo.each(row.cellObjects, function(r){
8423 _this.renderCellObject(r);
8430 var tfoot = this.el.select('tfoot', true).first();
8432 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8434 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8436 var total = this.ds.getTotalCount();
8438 if(this.footer.pageSize < total){
8439 this.mainFoot.show();
8443 Roo.each(this.el.select('tbody td', true).elements, function(e){
8444 e.on('mouseover', _this.onMouseover, _this);
8447 Roo.each(this.el.select('tbody td', true).elements, function(e){
8448 e.on('mouseout', _this.onMouseout, _this);
8450 this.fireEvent('rowsrendered', this);
8456 onUpdate : function(ds,record)
8458 this.refreshRow(record);
8462 onRemove : function(ds, record, index, isUpdate){
8463 if(isUpdate !== true){
8464 this.fireEvent("beforerowremoved", this, index, record);
8466 var bt = this.mainBody.dom;
8468 var rows = this.el.select('tbody > tr', true).elements;
8470 if(typeof(rows[index]) != 'undefined'){
8471 bt.removeChild(rows[index].dom);
8474 // if(bt.rows[index]){
8475 // bt.removeChild(bt.rows[index]);
8478 if(isUpdate !== true){
8479 //this.stripeRows(index);
8480 //this.syncRowHeights(index, index);
8482 this.fireEvent("rowremoved", this, index, record);
8486 onAdd : function(ds, records, rowIndex)
8488 //Roo.log('on Add called');
8489 // - note this does not handle multiple adding very well..
8490 var bt = this.mainBody.dom;
8491 for (var i =0 ; i < records.length;i++) {
8492 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8493 //Roo.log(records[i]);
8494 //Roo.log(this.store.getAt(rowIndex+i));
8495 this.insertRow(this.store, rowIndex + i, false);
8502 refreshRow : function(record){
8503 var ds = this.store, index;
8504 if(typeof record == 'number'){
8506 record = ds.getAt(index);
8508 index = ds.indexOf(record);
8510 return; // should not happen - but seems to
8513 this.insertRow(ds, index, true);
8515 this.onRemove(ds, record, index+1, true);
8517 //this.syncRowHeights(index, index);
8519 this.fireEvent("rowupdated", this, index, record);
8522 insertRow : function(dm, rowIndex, isUpdate){
8525 this.fireEvent("beforerowsinserted", this, rowIndex);
8527 //var s = this.getScrollState();
8528 var row = this.renderRow(this.cm, this.store, rowIndex);
8529 // insert before rowIndex..
8530 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8534 if(row.cellObjects.length){
8535 Roo.each(row.cellObjects, function(r){
8536 _this.renderCellObject(r);
8541 this.fireEvent("rowsinserted", this, rowIndex);
8542 //this.syncRowHeights(firstRow, lastRow);
8543 //this.stripeRows(firstRow);
8550 getRowDom : function(rowIndex)
8552 var rows = this.el.select('tbody > tr', true).elements;
8554 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8557 // returns the object tree for a tr..
8560 renderRow : function(cm, ds, rowIndex)
8562 var d = ds.getAt(rowIndex);
8566 cls : 'x-row-' + rowIndex,
8570 var cellObjects = [];
8572 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8573 var config = cm.config[i];
8575 var renderer = cm.getRenderer(i);
8579 if(typeof(renderer) !== 'undefined'){
8580 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8582 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8583 // and are rendered into the cells after the row is rendered - using the id for the element.
8585 if(typeof(value) === 'object'){
8595 rowIndex : rowIndex,
8600 this.fireEvent('rowclass', this, rowcfg);
8604 cls : rowcfg.rowClass + ' x-col-' + i,
8606 html: (typeof(value) === 'object') ? '' : value
8613 if(typeof(config.colspan) != 'undefined'){
8614 td.colspan = config.colspan;
8617 if(typeof(config.hidden) != 'undefined' && config.hidden){
8618 td.style += ' display:none;';
8621 if(typeof(config.align) != 'undefined' && config.align.length){
8622 td.style += ' text-align:' + config.align + ';';
8624 if(typeof(config.valign) != 'undefined' && config.valign.length){
8625 td.style += ' vertical-align:' + config.valign + ';';
8628 if(typeof(config.width) != 'undefined'){
8629 td.style += ' width:' + config.width + 'px;';
8632 if(typeof(config.cursor) != 'undefined'){
8633 td.style += ' cursor:' + config.cursor + ';';
8636 if(typeof(config.cls) != 'undefined'){
8637 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8640 ['xs','sm','md','lg'].map(function(size){
8642 if(typeof(config[size]) == 'undefined'){
8648 if (!config[size]) { // 0 = hidden
8649 // BS 4 '0' is treated as hide that column and below.
8650 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8654 td.cls += ' col-' + size + '-' + config[size] + (
8655 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8665 row.cellObjects = cellObjects;
8673 onBeforeLoad : function()
8682 this.el.select('tbody', true).first().dom.innerHTML = '';
8685 * Show or hide a row.
8686 * @param {Number} rowIndex to show or hide
8687 * @param {Boolean} state hide
8689 setRowVisibility : function(rowIndex, state)
8691 var bt = this.mainBody.dom;
8693 var rows = this.el.select('tbody > tr', true).elements;
8695 if(typeof(rows[rowIndex]) == 'undefined'){
8698 rows[rowIndex].dom.style.display = state ? '' : 'none';
8702 getSelectionModel : function(){
8704 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8706 return this.selModel;
8709 * Render the Roo.bootstrap object from renderder
8711 renderCellObject : function(r)
8715 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8717 var t = r.cfg.render(r.container);
8720 Roo.each(r.cfg.cn, function(c){
8722 container: t.getChildContainer(),
8725 _this.renderCellObject(child);
8730 getRowIndex : function(row)
8734 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8745 * Returns the grid's underlying element = used by panel.Grid
8746 * @return {Element} The element
8748 getGridEl : function(){
8752 * Forces a resize - used by panel.Grid
8753 * @return {Element} The element
8755 autoSize : function()
8757 //var ctr = Roo.get(this.container.dom.parentElement);
8758 var ctr = Roo.get(this.el.dom);
8760 var thd = this.getGridEl().select('thead',true).first();
8761 var tbd = this.getGridEl().select('tbody', true).first();
8762 var tfd = this.getGridEl().select('tfoot', true).first();
8764 var cw = ctr.getWidth();
8765 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8769 tbd.setWidth(ctr.getWidth());
8770 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8771 // this needs fixing for various usage - currently only hydra job advers I think..
8773 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8775 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8778 cw = Math.max(cw, this.totalWidth);
8779 this.getGridEl().select('tbody tr',true).setWidth(cw);
8781 // resize 'expandable coloumn?
8783 return; // we doe not have a view in this design..
8786 onBodyScroll: function()
8788 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8790 this.mainHead.setStyle({
8791 'position' : 'relative',
8792 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8798 var scrollHeight = this.mainBody.dom.scrollHeight;
8800 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8802 var height = this.mainBody.getHeight();
8804 if(scrollHeight - height == scrollTop) {
8806 var total = this.ds.getTotalCount();
8808 if(this.footer.cursor + this.footer.pageSize < total){
8810 this.footer.ds.load({
8812 start : this.footer.cursor + this.footer.pageSize,
8813 limit : this.footer.pageSize
8823 onHeaderChange : function()
8825 var header = this.renderHeader();
8826 var table = this.el.select('table', true).first();
8828 this.mainHead.remove();
8829 this.mainHead = table.createChild(header, this.mainBody, false);
8832 onHiddenChange : function(colModel, colIndex, hidden)
8834 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8835 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8837 this.CSS.updateRule(thSelector, "display", "");
8838 this.CSS.updateRule(tdSelector, "display", "");
8841 this.CSS.updateRule(thSelector, "display", "none");
8842 this.CSS.updateRule(tdSelector, "display", "none");
8845 this.onHeaderChange();
8849 setColumnWidth: function(col_index, width)
8851 // width = "md-2 xs-2..."
8852 if(!this.colModel.config[col_index]) {
8856 var w = width.split(" ");
8858 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8860 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8863 for(var j = 0; j < w.length; j++) {
8869 var size_cls = w[j].split("-");
8871 if(!Number.isInteger(size_cls[1] * 1)) {
8875 if(!this.colModel.config[col_index][size_cls[0]]) {
8879 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8883 h_row[0].classList.replace(
8884 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8885 "col-"+size_cls[0]+"-"+size_cls[1]
8888 for(var i = 0; i < rows.length; i++) {
8890 var size_cls = w[j].split("-");
8892 if(!Number.isInteger(size_cls[1] * 1)) {
8896 if(!this.colModel.config[col_index][size_cls[0]]) {
8900 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8904 rows[i].classList.replace(
8905 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8906 "col-"+size_cls[0]+"-"+size_cls[1]
8910 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8925 * @class Roo.bootstrap.TableCell
8926 * @extends Roo.bootstrap.Component
8927 * Bootstrap TableCell class
8928 * @cfg {String} html cell contain text
8929 * @cfg {String} cls cell class
8930 * @cfg {String} tag cell tag (td|th) default td
8931 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8932 * @cfg {String} align Aligns the content in a cell
8933 * @cfg {String} axis Categorizes cells
8934 * @cfg {String} bgcolor Specifies the background color of a cell
8935 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8936 * @cfg {Number} colspan Specifies the number of columns a cell should span
8937 * @cfg {String} headers Specifies one or more header cells a cell is related to
8938 * @cfg {Number} height Sets the height of a cell
8939 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8940 * @cfg {Number} rowspan Sets the number of rows a cell should span
8941 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8942 * @cfg {String} valign Vertical aligns the content in a cell
8943 * @cfg {Number} width Specifies the width of a cell
8946 * Create a new TableCell
8947 * @param {Object} config The config object
8950 Roo.bootstrap.TableCell = function(config){
8951 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8954 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8974 getAutoCreate : function(){
8975 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8995 cfg.align=this.align
9001 cfg.bgcolor=this.bgcolor
9004 cfg.charoff=this.charoff
9007 cfg.colspan=this.colspan
9010 cfg.headers=this.headers
9013 cfg.height=this.height
9016 cfg.nowrap=this.nowrap
9019 cfg.rowspan=this.rowspan
9022 cfg.scope=this.scope
9025 cfg.valign=this.valign
9028 cfg.width=this.width
9047 * @class Roo.bootstrap.TableRow
9048 * @extends Roo.bootstrap.Component
9049 * Bootstrap TableRow class
9050 * @cfg {String} cls row class
9051 * @cfg {String} align Aligns the content in a table row
9052 * @cfg {String} bgcolor Specifies a background color for a table row
9053 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9054 * @cfg {String} valign Vertical aligns the content in a table row
9057 * Create a new TableRow
9058 * @param {Object} config The config object
9061 Roo.bootstrap.TableRow = function(config){
9062 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9065 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9073 getAutoCreate : function(){
9074 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9084 cfg.align = this.align;
9087 cfg.bgcolor = this.bgcolor;
9090 cfg.charoff = this.charoff;
9093 cfg.valign = this.valign;
9111 * @class Roo.bootstrap.TableBody
9112 * @extends Roo.bootstrap.Component
9113 * Bootstrap TableBody class
9114 * @cfg {String} cls element class
9115 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9116 * @cfg {String} align Aligns the content inside the element
9117 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9118 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9121 * Create a new TableBody
9122 * @param {Object} config The config object
9125 Roo.bootstrap.TableBody = function(config){
9126 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9129 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9137 getAutoCreate : function(){
9138 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9152 cfg.align = this.align;
9155 cfg.charoff = this.charoff;
9158 cfg.valign = this.valign;
9165 // initEvents : function()
9172 // this.store = Roo.factory(this.store, Roo.data);
9173 // this.store.on('load', this.onLoad, this);
9175 // this.store.load();
9179 // onLoad: function ()
9181 // this.fireEvent('load', this);
9191 * Ext JS Library 1.1.1
9192 * Copyright(c) 2006-2007, Ext JS, LLC.
9194 * Originally Released Under LGPL - original licence link has changed is not relivant.
9197 * <script type="text/javascript">
9200 // as we use this in bootstrap.
9201 Roo.namespace('Roo.form');
9203 * @class Roo.form.Action
9204 * Internal Class used to handle form actions
9206 * @param {Roo.form.BasicForm} el The form element or its id
9207 * @param {Object} config Configuration options
9212 // define the action interface
9213 Roo.form.Action = function(form, options){
9215 this.options = options || {};
9218 * Client Validation Failed
9221 Roo.form.Action.CLIENT_INVALID = 'client';
9223 * Server Validation Failed
9226 Roo.form.Action.SERVER_INVALID = 'server';
9228 * Connect to Server Failed
9231 Roo.form.Action.CONNECT_FAILURE = 'connect';
9233 * Reading Data from Server Failed
9236 Roo.form.Action.LOAD_FAILURE = 'load';
9238 Roo.form.Action.prototype = {
9240 failureType : undefined,
9241 response : undefined,
9245 run : function(options){
9250 success : function(response){
9255 handleResponse : function(response){
9259 // default connection failure
9260 failure : function(response){
9262 this.response = response;
9263 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9264 this.form.afterAction(this, false);
9267 processResponse : function(response){
9268 this.response = response;
9269 if(!response.responseText){
9272 this.result = this.handleResponse(response);
9276 // utility functions used internally
9277 getUrl : function(appendParams){
9278 var url = this.options.url || this.form.url || this.form.el.dom.action;
9280 var p = this.getParams();
9282 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9288 getMethod : function(){
9289 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9292 getParams : function(){
9293 var bp = this.form.baseParams;
9294 var p = this.options.params;
9296 if(typeof p == "object"){
9297 p = Roo.urlEncode(Roo.applyIf(p, bp));
9298 }else if(typeof p == 'string' && bp){
9299 p += '&' + Roo.urlEncode(bp);
9302 p = Roo.urlEncode(bp);
9307 createCallback : function(){
9309 success: this.success,
9310 failure: this.failure,
9312 timeout: (this.form.timeout*1000),
9313 upload: this.form.fileUpload ? this.success : undefined
9318 Roo.form.Action.Submit = function(form, options){
9319 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9322 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9325 haveProgress : false,
9326 uploadComplete : false,
9328 // uploadProgress indicator.
9329 uploadProgress : function()
9331 if (!this.form.progressUrl) {
9335 if (!this.haveProgress) {
9336 Roo.MessageBox.progress("Uploading", "Uploading");
9338 if (this.uploadComplete) {
9339 Roo.MessageBox.hide();
9343 this.haveProgress = true;
9345 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9347 var c = new Roo.data.Connection();
9349 url : this.form.progressUrl,
9354 success : function(req){
9355 //console.log(data);
9359 rdata = Roo.decode(req.responseText)
9361 Roo.log("Invalid data from server..");
9365 if (!rdata || !rdata.success) {
9367 Roo.MessageBox.alert(Roo.encode(rdata));
9370 var data = rdata.data;
9372 if (this.uploadComplete) {
9373 Roo.MessageBox.hide();
9378 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9379 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9382 this.uploadProgress.defer(2000,this);
9385 failure: function(data) {
9386 Roo.log('progress url failed ');
9397 // run get Values on the form, so it syncs any secondary forms.
9398 this.form.getValues();
9400 var o = this.options;
9401 var method = this.getMethod();
9402 var isPost = method == 'POST';
9403 if(o.clientValidation === false || this.form.isValid()){
9405 if (this.form.progressUrl) {
9406 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9407 (new Date() * 1) + '' + Math.random());
9412 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9413 form:this.form.el.dom,
9414 url:this.getUrl(!isPost),
9416 params:isPost ? this.getParams() : null,
9417 isUpload: this.form.fileUpload,
9418 formData : this.form.formData
9421 this.uploadProgress();
9423 }else if (o.clientValidation !== false){ // client validation failed
9424 this.failureType = Roo.form.Action.CLIENT_INVALID;
9425 this.form.afterAction(this, false);
9429 success : function(response)
9431 this.uploadComplete= true;
9432 if (this.haveProgress) {
9433 Roo.MessageBox.hide();
9437 var result = this.processResponse(response);
9438 if(result === true || result.success){
9439 this.form.afterAction(this, true);
9443 this.form.markInvalid(result.errors);
9444 this.failureType = Roo.form.Action.SERVER_INVALID;
9446 this.form.afterAction(this, false);
9448 failure : function(response)
9450 this.uploadComplete= true;
9451 if (this.haveProgress) {
9452 Roo.MessageBox.hide();
9455 this.response = response;
9456 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9457 this.form.afterAction(this, false);
9460 handleResponse : function(response){
9461 if(this.form.errorReader){
9462 var rs = this.form.errorReader.read(response);
9465 for(var i = 0, len = rs.records.length; i < len; i++) {
9466 var r = rs.records[i];
9470 if(errors.length < 1){
9474 success : rs.success,
9480 ret = Roo.decode(response.responseText);
9484 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9494 Roo.form.Action.Load = function(form, options){
9495 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9496 this.reader = this.form.reader;
9499 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9504 Roo.Ajax.request(Roo.apply(
9505 this.createCallback(), {
9506 method:this.getMethod(),
9507 url:this.getUrl(false),
9508 params:this.getParams()
9512 success : function(response){
9514 var result = this.processResponse(response);
9515 if(result === true || !result.success || !result.data){
9516 this.failureType = Roo.form.Action.LOAD_FAILURE;
9517 this.form.afterAction(this, false);
9520 this.form.clearInvalid();
9521 this.form.setValues(result.data);
9522 this.form.afterAction(this, true);
9525 handleResponse : function(response){
9526 if(this.form.reader){
9527 var rs = this.form.reader.read(response);
9528 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9530 success : rs.success,
9534 return Roo.decode(response.responseText);
9538 Roo.form.Action.ACTION_TYPES = {
9539 'load' : Roo.form.Action.Load,
9540 'submit' : Roo.form.Action.Submit
9549 * @class Roo.bootstrap.Form
9550 * @extends Roo.bootstrap.Component
9551 * Bootstrap Form class
9552 * @cfg {String} method GET | POST (default POST)
9553 * @cfg {String} labelAlign top | left (default top)
9554 * @cfg {String} align left | right - for navbars
9555 * @cfg {Boolean} loadMask load mask when submit (default true)
9560 * @param {Object} config The config object
9564 Roo.bootstrap.Form = function(config){
9566 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9568 Roo.bootstrap.Form.popover.apply();
9572 * @event clientvalidation
9573 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9574 * @param {Form} this
9575 * @param {Boolean} valid true if the form has passed client-side validation
9577 clientvalidation: true,
9579 * @event beforeaction
9580 * Fires before any action is performed. Return false to cancel the action.
9581 * @param {Form} this
9582 * @param {Action} action The action to be performed
9586 * @event actionfailed
9587 * Fires when an action fails.
9588 * @param {Form} this
9589 * @param {Action} action The action that failed
9591 actionfailed : true,
9593 * @event actioncomplete
9594 * Fires when an action is completed.
9595 * @param {Form} this
9596 * @param {Action} action The action that completed
9598 actioncomplete : true
9602 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9605 * @cfg {String} method
9606 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9611 * The URL to use for form actions if one isn't supplied in the action options.
9614 * @cfg {Boolean} fileUpload
9615 * Set to true if this form is a file upload.
9619 * @cfg {Object} baseParams
9620 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9624 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9628 * @cfg {Sting} align (left|right) for navbar forms
9633 activeAction : null,
9636 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9637 * element by passing it or its id or mask the form itself by passing in true.
9640 waitMsgTarget : false,
9645 * @cfg {Boolean} errorMask (true|false) default false
9650 * @cfg {Number} maskOffset Default 100
9655 * @cfg {Boolean} maskBody
9659 getAutoCreate : function(){
9663 method : this.method || 'POST',
9664 id : this.id || Roo.id(),
9667 if (this.parent().xtype.match(/^Nav/)) {
9668 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9672 if (this.labelAlign == 'left' ) {
9673 cfg.cls += ' form-horizontal';
9679 initEvents : function()
9681 this.el.on('submit', this.onSubmit, this);
9682 // this was added as random key presses on the form where triggering form submit.
9683 this.el.on('keypress', function(e) {
9684 if (e.getCharCode() != 13) {
9687 // we might need to allow it for textareas.. and some other items.
9688 // check e.getTarget().
9690 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9694 Roo.log("keypress blocked");
9702 onSubmit : function(e){
9707 * Returns true if client-side validation on the form is successful.
9710 isValid : function(){
9711 var items = this.getItems();
9715 items.each(function(f){
9721 Roo.log('invalid field: ' + f.name);
9725 if(!target && f.el.isVisible(true)){
9731 if(this.errorMask && !valid){
9732 Roo.bootstrap.Form.popover.mask(this, target);
9739 * Returns true if any fields in this form have changed since their original load.
9742 isDirty : function(){
9744 var items = this.getItems();
9745 items.each(function(f){
9755 * Performs a predefined action (submit or load) or custom actions you define on this form.
9756 * @param {String} actionName The name of the action type
9757 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9758 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9759 * accept other config options):
9761 Property Type Description
9762 ---------------- --------------- ----------------------------------------------------------------------------------
9763 url String The url for the action (defaults to the form's url)
9764 method String The form method to use (defaults to the form's method, or POST if not defined)
9765 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9766 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9767 validate the form on the client (defaults to false)
9769 * @return {BasicForm} this
9771 doAction : function(action, options){
9772 if(typeof action == 'string'){
9773 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9775 if(this.fireEvent('beforeaction', this, action) !== false){
9776 this.beforeAction(action);
9777 action.run.defer(100, action);
9783 beforeAction : function(action){
9784 var o = action.options;
9789 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9791 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9794 // not really supported yet.. ??
9796 //if(this.waitMsgTarget === true){
9797 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9798 //}else if(this.waitMsgTarget){
9799 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9800 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9802 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9808 afterAction : function(action, success){
9809 this.activeAction = null;
9810 var o = action.options;
9815 Roo.get(document.body).unmask();
9821 //if(this.waitMsgTarget === true){
9822 // this.el.unmask();
9823 //}else if(this.waitMsgTarget){
9824 // this.waitMsgTarget.unmask();
9826 // Roo.MessageBox.updateProgress(1);
9827 // Roo.MessageBox.hide();
9834 Roo.callback(o.success, o.scope, [this, action]);
9835 this.fireEvent('actioncomplete', this, action);
9839 // failure condition..
9840 // we have a scenario where updates need confirming.
9841 // eg. if a locking scenario exists..
9842 // we look for { errors : { needs_confirm : true }} in the response.
9844 (typeof(action.result) != 'undefined') &&
9845 (typeof(action.result.errors) != 'undefined') &&
9846 (typeof(action.result.errors.needs_confirm) != 'undefined')
9849 Roo.log("not supported yet");
9852 Roo.MessageBox.confirm(
9853 "Change requires confirmation",
9854 action.result.errorMsg,
9859 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9869 Roo.callback(o.failure, o.scope, [this, action]);
9870 // show an error message if no failed handler is set..
9871 if (!this.hasListener('actionfailed')) {
9872 Roo.log("need to add dialog support");
9874 Roo.MessageBox.alert("Error",
9875 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9876 action.result.errorMsg :
9877 "Saving Failed, please check your entries or try again"
9882 this.fireEvent('actionfailed', this, action);
9887 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9888 * @param {String} id The value to search for
9891 findField : function(id){
9892 var items = this.getItems();
9893 var field = items.get(id);
9895 items.each(function(f){
9896 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9903 return field || null;
9906 * Mark fields in this form invalid in bulk.
9907 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9908 * @return {BasicForm} this
9910 markInvalid : function(errors){
9911 if(errors instanceof Array){
9912 for(var i = 0, len = errors.length; i < len; i++){
9913 var fieldError = errors[i];
9914 var f = this.findField(fieldError.id);
9916 f.markInvalid(fieldError.msg);
9922 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9923 field.markInvalid(errors[id]);
9927 //Roo.each(this.childForms || [], function (f) {
9928 // f.markInvalid(errors);
9935 * Set values for fields in this form in bulk.
9936 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9937 * @return {BasicForm} this
9939 setValues : function(values){
9940 if(values instanceof Array){ // array of objects
9941 for(var i = 0, len = values.length; i < len; i++){
9943 var f = this.findField(v.id);
9945 f.setValue(v.value);
9946 if(this.trackResetOnLoad){
9947 f.originalValue = f.getValue();
9951 }else{ // object hash
9954 if(typeof values[id] != 'function' && (field = this.findField(id))){
9956 if (field.setFromData &&
9958 field.displayField &&
9959 // combos' with local stores can
9960 // be queried via setValue()
9961 // to set their value..
9962 (field.store && !field.store.isLocal)
9966 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9967 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9968 field.setFromData(sd);
9970 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9972 field.setFromData(values);
9975 field.setValue(values[id]);
9979 if(this.trackResetOnLoad){
9980 field.originalValue = field.getValue();
9986 //Roo.each(this.childForms || [], function (f) {
9987 // f.setValues(values);
9994 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9995 * they are returned as an array.
9996 * @param {Boolean} asString
9999 getValues : function(asString){
10000 //if (this.childForms) {
10001 // copy values from the child forms
10002 // Roo.each(this.childForms, function (f) {
10003 // this.setValues(f.getValues());
10009 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10010 if(asString === true){
10013 return Roo.urlDecode(fs);
10017 * Returns the fields in this form as an object with key/value pairs.
10018 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10021 getFieldValues : function(with_hidden)
10023 var items = this.getItems();
10025 items.each(function(f){
10027 if (!f.getName()) {
10031 var v = f.getValue();
10033 if (f.inputType =='radio') {
10034 if (typeof(ret[f.getName()]) == 'undefined') {
10035 ret[f.getName()] = ''; // empty..
10038 if (!f.el.dom.checked) {
10042 v = f.el.dom.value;
10046 if(f.xtype == 'MoneyField'){
10047 ret[f.currencyName] = f.getCurrency();
10050 // not sure if this supported any more..
10051 if ((typeof(v) == 'object') && f.getRawValue) {
10052 v = f.getRawValue() ; // dates..
10054 // combo boxes where name != hiddenName...
10055 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10056 ret[f.name] = f.getRawValue();
10058 ret[f.getName()] = v;
10065 * Clears all invalid messages in this form.
10066 * @return {BasicForm} this
10068 clearInvalid : function(){
10069 var items = this.getItems();
10071 items.each(function(f){
10079 * Resets this form.
10080 * @return {BasicForm} this
10082 reset : function(){
10083 var items = this.getItems();
10084 items.each(function(f){
10088 Roo.each(this.childForms || [], function (f) {
10096 getItems : function()
10098 var r=new Roo.util.MixedCollection(false, function(o){
10099 return o.id || (o.id = Roo.id());
10101 var iter = function(el) {
10108 Roo.each(el.items,function(e) {
10117 hideFields : function(items)
10119 Roo.each(items, function(i){
10121 var f = this.findField(i);
10132 showFields : function(items)
10134 Roo.each(items, function(i){
10136 var f = this.findField(i);
10149 Roo.apply(Roo.bootstrap.Form, {
10165 intervalID : false,
10171 if(this.isApplied){
10176 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10177 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10178 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10179 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10182 this.maskEl.top.enableDisplayMode("block");
10183 this.maskEl.left.enableDisplayMode("block");
10184 this.maskEl.bottom.enableDisplayMode("block");
10185 this.maskEl.right.enableDisplayMode("block");
10187 this.toolTip = new Roo.bootstrap.Tooltip({
10188 cls : 'roo-form-error-popover',
10190 'left' : ['r-l', [-2,0], 'right'],
10191 'right' : ['l-r', [2,0], 'left'],
10192 'bottom' : ['tl-bl', [0,2], 'top'],
10193 'top' : [ 'bl-tl', [0,-2], 'bottom']
10197 this.toolTip.render(Roo.get(document.body));
10199 this.toolTip.el.enableDisplayMode("block");
10201 Roo.get(document.body).on('click', function(){
10205 Roo.get(document.body).on('touchstart', function(){
10209 this.isApplied = true
10212 mask : function(form, target)
10216 this.target = target;
10218 if(!this.form.errorMask || !target.el){
10222 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10224 Roo.log(scrollable);
10226 var ot = this.target.el.calcOffsetsTo(scrollable);
10228 var scrollTo = ot[1] - this.form.maskOffset;
10230 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10232 scrollable.scrollTo('top', scrollTo);
10234 var box = this.target.el.getBox();
10236 var zIndex = Roo.bootstrap.Modal.zIndex++;
10239 this.maskEl.top.setStyle('position', 'absolute');
10240 this.maskEl.top.setStyle('z-index', zIndex);
10241 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10242 this.maskEl.top.setLeft(0);
10243 this.maskEl.top.setTop(0);
10244 this.maskEl.top.show();
10246 this.maskEl.left.setStyle('position', 'absolute');
10247 this.maskEl.left.setStyle('z-index', zIndex);
10248 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10249 this.maskEl.left.setLeft(0);
10250 this.maskEl.left.setTop(box.y - this.padding);
10251 this.maskEl.left.show();
10253 this.maskEl.bottom.setStyle('position', 'absolute');
10254 this.maskEl.bottom.setStyle('z-index', zIndex);
10255 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10256 this.maskEl.bottom.setLeft(0);
10257 this.maskEl.bottom.setTop(box.bottom + this.padding);
10258 this.maskEl.bottom.show();
10260 this.maskEl.right.setStyle('position', 'absolute');
10261 this.maskEl.right.setStyle('z-index', zIndex);
10262 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10263 this.maskEl.right.setLeft(box.right + this.padding);
10264 this.maskEl.right.setTop(box.y - this.padding);
10265 this.maskEl.right.show();
10267 this.toolTip.bindEl = this.target.el;
10269 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10271 var tip = this.target.blankText;
10273 if(this.target.getValue() !== '' ) {
10275 if (this.target.invalidText.length) {
10276 tip = this.target.invalidText;
10277 } else if (this.target.regexText.length){
10278 tip = this.target.regexText;
10282 this.toolTip.show(tip);
10284 this.intervalID = window.setInterval(function() {
10285 Roo.bootstrap.Form.popover.unmask();
10288 window.onwheel = function(){ return false;};
10290 (function(){ this.isMasked = true; }).defer(500, this);
10294 unmask : function()
10296 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10300 this.maskEl.top.setStyle('position', 'absolute');
10301 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10302 this.maskEl.top.hide();
10304 this.maskEl.left.setStyle('position', 'absolute');
10305 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10306 this.maskEl.left.hide();
10308 this.maskEl.bottom.setStyle('position', 'absolute');
10309 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10310 this.maskEl.bottom.hide();
10312 this.maskEl.right.setStyle('position', 'absolute');
10313 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10314 this.maskEl.right.hide();
10316 this.toolTip.hide();
10318 this.toolTip.el.hide();
10320 window.onwheel = function(){ return true;};
10322 if(this.intervalID){
10323 window.clearInterval(this.intervalID);
10324 this.intervalID = false;
10327 this.isMasked = false;
10337 * Ext JS Library 1.1.1
10338 * Copyright(c) 2006-2007, Ext JS, LLC.
10340 * Originally Released Under LGPL - original licence link has changed is not relivant.
10343 * <script type="text/javascript">
10346 * @class Roo.form.VTypes
10347 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10350 Roo.form.VTypes = function(){
10351 // closure these in so they are only created once.
10352 var alpha = /^[a-zA-Z_]+$/;
10353 var alphanum = /^[a-zA-Z0-9_]+$/;
10354 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10355 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10357 // All these messages and functions are configurable
10360 * The function used to validate email addresses
10361 * @param {String} value The email address
10363 'email' : function(v){
10364 return email.test(v);
10367 * The error text to display when the email validation function returns false
10370 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10372 * The keystroke filter mask to be applied on email input
10375 'emailMask' : /[a-z0-9_\.\-@]/i,
10378 * The function used to validate URLs
10379 * @param {String} value The URL
10381 'url' : function(v){
10382 return url.test(v);
10385 * The error text to display when the url validation function returns false
10388 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10391 * The function used to validate alpha values
10392 * @param {String} value The value
10394 'alpha' : function(v){
10395 return alpha.test(v);
10398 * The error text to display when the alpha validation function returns false
10401 'alphaText' : 'This field should only contain letters and _',
10403 * The keystroke filter mask to be applied on alpha input
10406 'alphaMask' : /[a-z_]/i,
10409 * The function used to validate alphanumeric values
10410 * @param {String} value The value
10412 'alphanum' : function(v){
10413 return alphanum.test(v);
10416 * The error text to display when the alphanumeric validation function returns false
10419 'alphanumText' : 'This field should only contain letters, numbers and _',
10421 * The keystroke filter mask to be applied on alphanumeric input
10424 'alphanumMask' : /[a-z0-9_]/i
10434 * @class Roo.bootstrap.Input
10435 * @extends Roo.bootstrap.Component
10436 * Bootstrap Input class
10437 * @cfg {Boolean} disabled is it disabled
10438 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10439 * @cfg {String} name name of the input
10440 * @cfg {string} fieldLabel - the label associated
10441 * @cfg {string} placeholder - placeholder to put in text.
10442 * @cfg {string} before - input group add on before
10443 * @cfg {string} after - input group add on after
10444 * @cfg {string} size - (lg|sm) or leave empty..
10445 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10446 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10447 * @cfg {Number} md colspan out of 12 for computer-sized screens
10448 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10449 * @cfg {string} value default value of the input
10450 * @cfg {Number} labelWidth set the width of label
10451 * @cfg {Number} labellg set the width of label (1-12)
10452 * @cfg {Number} labelmd set the width of label (1-12)
10453 * @cfg {Number} labelsm set the width of label (1-12)
10454 * @cfg {Number} labelxs set the width of label (1-12)
10455 * @cfg {String} labelAlign (top|left)
10456 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10457 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10458 * @cfg {String} indicatorpos (left|right) default left
10459 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10460 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10461 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10463 * @cfg {String} align (left|center|right) Default left
10464 * @cfg {Boolean} forceFeedback (true|false) Default false
10467 * Create a new Input
10468 * @param {Object} config The config object
10471 Roo.bootstrap.Input = function(config){
10473 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10478 * Fires when this field receives input focus.
10479 * @param {Roo.form.Field} this
10484 * Fires when this field loses input focus.
10485 * @param {Roo.form.Field} this
10489 * @event specialkey
10490 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10491 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10492 * @param {Roo.form.Field} this
10493 * @param {Roo.EventObject} e The event object
10498 * Fires just before the field blurs if the field value has changed.
10499 * @param {Roo.form.Field} this
10500 * @param {Mixed} newValue The new value
10501 * @param {Mixed} oldValue The original value
10506 * Fires after the field has been marked as invalid.
10507 * @param {Roo.form.Field} this
10508 * @param {String} msg The validation message
10513 * Fires after the field has been validated with no errors.
10514 * @param {Roo.form.Field} this
10519 * Fires after the key up
10520 * @param {Roo.form.Field} this
10521 * @param {Roo.EventObject} e The event Object
10526 * Fires after the user pastes into input
10527 * @param {Roo.form.Field} this
10528 * @param {Roo.EventObject} e The event Object
10534 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10536 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10537 automatic validation (defaults to "keyup").
10539 validationEvent : "keyup",
10541 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10543 validateOnBlur : true,
10545 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10547 validationDelay : 250,
10549 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10551 focusClass : "x-form-focus", // not needed???
10555 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10557 invalidClass : "has-warning",
10560 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10562 validClass : "has-success",
10565 * @cfg {Boolean} hasFeedback (true|false) default true
10567 hasFeedback : true,
10570 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10572 invalidFeedbackClass : "glyphicon-warning-sign",
10575 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10577 validFeedbackClass : "glyphicon-ok",
10580 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10582 selectOnFocus : false,
10585 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10589 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10594 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10596 disableKeyFilter : false,
10599 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10603 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10607 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10609 blankText : "Please complete this mandatory field",
10612 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10616 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10618 maxLength : Number.MAX_VALUE,
10620 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10622 minLengthText : "The minimum length for this field is {0}",
10624 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10626 maxLengthText : "The maximum length for this field is {0}",
10630 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10631 * If available, this function will be called only after the basic validators all return true, and will be passed the
10632 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10636 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10637 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10638 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10642 * @cfg {String} regexText -- Depricated - use Invalid Text
10647 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10653 autocomplete: false,
10657 inputType : 'text',
10660 placeholder: false,
10665 preventMark: false,
10666 isFormField : true,
10669 labelAlign : false,
10672 formatedValue : false,
10673 forceFeedback : false,
10675 indicatorpos : 'left',
10685 parentLabelAlign : function()
10688 while (parent.parent()) {
10689 parent = parent.parent();
10690 if (typeof(parent.labelAlign) !='undefined') {
10691 return parent.labelAlign;
10698 getAutoCreate : function()
10700 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10706 if(this.inputType != 'hidden'){
10707 cfg.cls = 'form-group' //input-group
10713 type : this.inputType,
10714 value : this.value,
10715 cls : 'form-control',
10716 placeholder : this.placeholder || '',
10717 autocomplete : this.autocomplete || 'new-password'
10719 if (this.inputType == 'file') {
10720 input.style = 'overflow:hidden'; // why not in CSS?
10723 if(this.capture.length){
10724 input.capture = this.capture;
10727 if(this.accept.length){
10728 input.accept = this.accept + "/*";
10732 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10735 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10736 input.maxLength = this.maxLength;
10739 if (this.disabled) {
10740 input.disabled=true;
10743 if (this.readOnly) {
10744 input.readonly=true;
10748 input.name = this.name;
10752 input.cls += ' input-' + this.size;
10756 ['xs','sm','md','lg'].map(function(size){
10757 if (settings[size]) {
10758 cfg.cls += ' col-' + size + '-' + settings[size];
10762 var inputblock = input;
10766 cls: 'glyphicon form-control-feedback'
10769 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10772 cls : 'has-feedback',
10780 if (this.before || this.after) {
10783 cls : 'input-group',
10787 if (this.before && typeof(this.before) == 'string') {
10789 inputblock.cn.push({
10791 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10795 if (this.before && typeof(this.before) == 'object') {
10796 this.before = Roo.factory(this.before);
10798 inputblock.cn.push({
10800 cls : 'roo-input-before input-group-prepend input-group-' +
10801 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10805 inputblock.cn.push(input);
10807 if (this.after && typeof(this.after) == 'string') {
10808 inputblock.cn.push({
10810 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10814 if (this.after && typeof(this.after) == 'object') {
10815 this.after = Roo.factory(this.after);
10817 inputblock.cn.push({
10819 cls : 'roo-input-after input-group-append input-group-' +
10820 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10824 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10825 inputblock.cls += ' has-feedback';
10826 inputblock.cn.push(feedback);
10831 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10832 tooltip : 'This field is required'
10834 if (this.allowBlank ) {
10835 indicator.style = this.allowBlank ? ' display:none' : '';
10837 if (align ==='left' && this.fieldLabel.length) {
10839 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10846 cls : 'control-label col-form-label',
10847 html : this.fieldLabel
10858 var labelCfg = cfg.cn[1];
10859 var contentCfg = cfg.cn[2];
10861 if(this.indicatorpos == 'right'){
10866 cls : 'control-label col-form-label',
10870 html : this.fieldLabel
10884 labelCfg = cfg.cn[0];
10885 contentCfg = cfg.cn[1];
10889 if(this.labelWidth > 12){
10890 labelCfg.style = "width: " + this.labelWidth + 'px';
10893 if(this.labelWidth < 13 && this.labelmd == 0){
10894 this.labelmd = this.labelWidth;
10897 if(this.labellg > 0){
10898 labelCfg.cls += ' col-lg-' + this.labellg;
10899 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10902 if(this.labelmd > 0){
10903 labelCfg.cls += ' col-md-' + this.labelmd;
10904 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10907 if(this.labelsm > 0){
10908 labelCfg.cls += ' col-sm-' + this.labelsm;
10909 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10912 if(this.labelxs > 0){
10913 labelCfg.cls += ' col-xs-' + this.labelxs;
10914 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10918 } else if ( this.fieldLabel.length) {
10925 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10926 tooltip : 'This field is required',
10927 style : this.allowBlank ? ' display:none' : ''
10931 //cls : 'input-group-addon',
10932 html : this.fieldLabel
10940 if(this.indicatorpos == 'right'){
10945 //cls : 'input-group-addon',
10946 html : this.fieldLabel
10951 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10952 tooltip : 'This field is required',
10953 style : this.allowBlank ? ' display:none' : ''
10973 if (this.parentType === 'Navbar' && this.parent().bar) {
10974 cfg.cls += ' navbar-form';
10977 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10978 // on BS4 we do this only if not form
10979 cfg.cls += ' navbar-form';
10987 * return the real input element.
10989 inputEl: function ()
10991 return this.el.select('input.form-control',true).first();
10994 tooltipEl : function()
10996 return this.inputEl();
10999 indicatorEl : function()
11001 if (Roo.bootstrap.version == 4) {
11002 return false; // not enabled in v4 yet.
11005 var indicator = this.el.select('i.roo-required-indicator',true).first();
11015 setDisabled : function(v)
11017 var i = this.inputEl().dom;
11019 i.removeAttribute('disabled');
11023 i.setAttribute('disabled','true');
11025 initEvents : function()
11028 this.inputEl().on("keydown" , this.fireKey, this);
11029 this.inputEl().on("focus", this.onFocus, this);
11030 this.inputEl().on("blur", this.onBlur, this);
11032 this.inputEl().relayEvent('keyup', this);
11033 this.inputEl().relayEvent('paste', this);
11035 this.indicator = this.indicatorEl();
11037 if(this.indicator){
11038 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11041 // reference to original value for reset
11042 this.originalValue = this.getValue();
11043 //Roo.form.TextField.superclass.initEvents.call(this);
11044 if(this.validationEvent == 'keyup'){
11045 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11046 this.inputEl().on('keyup', this.filterValidation, this);
11048 else if(this.validationEvent !== false){
11049 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11052 if(this.selectOnFocus){
11053 this.on("focus", this.preFocus, this);
11056 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11057 this.inputEl().on("keypress", this.filterKeys, this);
11059 this.inputEl().relayEvent('keypress', this);
11062 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11063 this.el.on("click", this.autoSize, this);
11066 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11067 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11070 if (typeof(this.before) == 'object') {
11071 this.before.render(this.el.select('.roo-input-before',true).first());
11073 if (typeof(this.after) == 'object') {
11074 this.after.render(this.el.select('.roo-input-after',true).first());
11077 this.inputEl().on('change', this.onChange, this);
11080 filterValidation : function(e){
11081 if(!e.isNavKeyPress()){
11082 this.validationTask.delay(this.validationDelay);
11086 * Validates the field value
11087 * @return {Boolean} True if the value is valid, else false
11089 validate : function(){
11090 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11091 if(this.disabled || this.validateValue(this.getRawValue())){
11096 this.markInvalid();
11102 * Validates a value according to the field's validation rules and marks the field as invalid
11103 * if the validation fails
11104 * @param {Mixed} value The value to validate
11105 * @return {Boolean} True if the value is valid, else false
11107 validateValue : function(value)
11109 if(this.getVisibilityEl().hasClass('hidden')){
11113 if(value.length < 1) { // if it's blank
11114 if(this.allowBlank){
11120 if(value.length < this.minLength){
11123 if(value.length > this.maxLength){
11127 var vt = Roo.form.VTypes;
11128 if(!vt[this.vtype](value, this)){
11132 if(typeof this.validator == "function"){
11133 var msg = this.validator(value);
11137 if (typeof(msg) == 'string') {
11138 this.invalidText = msg;
11142 if(this.regex && !this.regex.test(value)){
11150 fireKey : function(e){
11151 //Roo.log('field ' + e.getKey());
11152 if(e.isNavKeyPress()){
11153 this.fireEvent("specialkey", this, e);
11156 focus : function (selectText){
11158 this.inputEl().focus();
11159 if(selectText === true){
11160 this.inputEl().dom.select();
11166 onFocus : function(){
11167 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11168 // this.el.addClass(this.focusClass);
11170 if(!this.hasFocus){
11171 this.hasFocus = true;
11172 this.startValue = this.getValue();
11173 this.fireEvent("focus", this);
11177 beforeBlur : Roo.emptyFn,
11181 onBlur : function(){
11183 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11184 //this.el.removeClass(this.focusClass);
11186 this.hasFocus = false;
11187 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11190 var v = this.getValue();
11191 if(String(v) !== String(this.startValue)){
11192 this.fireEvent('change', this, v, this.startValue);
11194 this.fireEvent("blur", this);
11197 onChange : function(e)
11199 var v = this.getValue();
11200 if(String(v) !== String(this.startValue)){
11201 this.fireEvent('change', this, v, this.startValue);
11207 * Resets the current field value to the originally loaded value and clears any validation messages
11209 reset : function(){
11210 this.setValue(this.originalValue);
11214 * Returns the name of the field
11215 * @return {Mixed} name The name field
11217 getName: function(){
11221 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11222 * @return {Mixed} value The field value
11224 getValue : function(){
11226 var v = this.inputEl().getValue();
11231 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11232 * @return {Mixed} value The field value
11234 getRawValue : function(){
11235 var v = this.inputEl().getValue();
11241 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11242 * @param {Mixed} value The value to set
11244 setRawValue : function(v){
11245 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11248 selectText : function(start, end){
11249 var v = this.getRawValue();
11251 start = start === undefined ? 0 : start;
11252 end = end === undefined ? v.length : end;
11253 var d = this.inputEl().dom;
11254 if(d.setSelectionRange){
11255 d.setSelectionRange(start, end);
11256 }else if(d.createTextRange){
11257 var range = d.createTextRange();
11258 range.moveStart("character", start);
11259 range.moveEnd("character", v.length-end);
11266 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11267 * @param {Mixed} value The value to set
11269 setValue : function(v){
11272 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11278 processValue : function(value){
11279 if(this.stripCharsRe){
11280 var newValue = value.replace(this.stripCharsRe, '');
11281 if(newValue !== value){
11282 this.setRawValue(newValue);
11289 preFocus : function(){
11291 if(this.selectOnFocus){
11292 this.inputEl().dom.select();
11295 filterKeys : function(e){
11296 var k = e.getKey();
11297 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11300 var c = e.getCharCode(), cc = String.fromCharCode(c);
11301 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11304 if(!this.maskRe.test(cc)){
11309 * Clear any invalid styles/messages for this field
11311 clearInvalid : function(){
11313 if(!this.el || this.preventMark){ // not rendered
11318 this.el.removeClass([this.invalidClass, 'is-invalid']);
11320 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11322 var feedback = this.el.select('.form-control-feedback', true).first();
11325 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11330 if(this.indicator){
11331 this.indicator.removeClass('visible');
11332 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11335 this.fireEvent('valid', this);
11339 * Mark this field as valid
11341 markValid : function()
11343 if(!this.el || this.preventMark){ // not rendered...
11347 this.el.removeClass([this.invalidClass, this.validClass]);
11348 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11350 var feedback = this.el.select('.form-control-feedback', true).first();
11353 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11356 if(this.indicator){
11357 this.indicator.removeClass('visible');
11358 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11366 if(this.allowBlank && !this.getRawValue().length){
11369 if (Roo.bootstrap.version == 3) {
11370 this.el.addClass(this.validClass);
11372 this.inputEl().addClass('is-valid');
11375 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11377 var feedback = this.el.select('.form-control-feedback', true).first();
11380 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11381 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11386 this.fireEvent('valid', this);
11390 * Mark this field as invalid
11391 * @param {String} msg The validation message
11393 markInvalid : function(msg)
11395 if(!this.el || this.preventMark){ // not rendered
11399 this.el.removeClass([this.invalidClass, this.validClass]);
11400 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11402 var feedback = this.el.select('.form-control-feedback', true).first();
11405 this.el.select('.form-control-feedback', true).first().removeClass(
11406 [this.invalidFeedbackClass, this.validFeedbackClass]);
11413 if(this.allowBlank && !this.getRawValue().length){
11417 if(this.indicator){
11418 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11419 this.indicator.addClass('visible');
11421 if (Roo.bootstrap.version == 3) {
11422 this.el.addClass(this.invalidClass);
11424 this.inputEl().addClass('is-invalid');
11429 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11431 var feedback = this.el.select('.form-control-feedback', true).first();
11434 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11436 if(this.getValue().length || this.forceFeedback){
11437 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11444 this.fireEvent('invalid', this, msg);
11447 SafariOnKeyDown : function(event)
11449 // this is a workaround for a password hang bug on chrome/ webkit.
11450 if (this.inputEl().dom.type != 'password') {
11454 var isSelectAll = false;
11456 if(this.inputEl().dom.selectionEnd > 0){
11457 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11459 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11460 event.preventDefault();
11465 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11467 event.preventDefault();
11468 // this is very hacky as keydown always get's upper case.
11470 var cc = String.fromCharCode(event.getCharCode());
11471 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11475 adjustWidth : function(tag, w){
11476 tag = tag.toLowerCase();
11477 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11478 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11479 if(tag == 'input'){
11482 if(tag == 'textarea'){
11485 }else if(Roo.isOpera){
11486 if(tag == 'input'){
11489 if(tag == 'textarea'){
11497 setFieldLabel : function(v)
11499 if(!this.rendered){
11503 if(this.indicatorEl()){
11504 var ar = this.el.select('label > span',true);
11506 if (ar.elements.length) {
11507 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11508 this.fieldLabel = v;
11512 var br = this.el.select('label',true);
11514 if(br.elements.length) {
11515 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11516 this.fieldLabel = v;
11520 Roo.log('Cannot Found any of label > span || label in input');
11524 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11525 this.fieldLabel = v;
11540 * @class Roo.bootstrap.TextArea
11541 * @extends Roo.bootstrap.Input
11542 * Bootstrap TextArea class
11543 * @cfg {Number} cols Specifies the visible width of a text area
11544 * @cfg {Number} rows Specifies the visible number of lines in a text area
11545 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11546 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11547 * @cfg {string} html text
11550 * Create a new TextArea
11551 * @param {Object} config The config object
11554 Roo.bootstrap.TextArea = function(config){
11555 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11559 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11569 getAutoCreate : function(){
11571 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11577 if(this.inputType != 'hidden'){
11578 cfg.cls = 'form-group' //input-group
11586 value : this.value || '',
11587 html: this.html || '',
11588 cls : 'form-control',
11589 placeholder : this.placeholder || ''
11593 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11594 input.maxLength = this.maxLength;
11598 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11602 input.cols = this.cols;
11605 if (this.readOnly) {
11606 input.readonly = true;
11610 input.name = this.name;
11614 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11618 ['xs','sm','md','lg'].map(function(size){
11619 if (settings[size]) {
11620 cfg.cls += ' col-' + size + '-' + settings[size];
11624 var inputblock = input;
11626 if(this.hasFeedback && !this.allowBlank){
11630 cls: 'glyphicon form-control-feedback'
11634 cls : 'has-feedback',
11643 if (this.before || this.after) {
11646 cls : 'input-group',
11650 inputblock.cn.push({
11652 cls : 'input-group-addon',
11657 inputblock.cn.push(input);
11659 if(this.hasFeedback && !this.allowBlank){
11660 inputblock.cls += ' has-feedback';
11661 inputblock.cn.push(feedback);
11665 inputblock.cn.push({
11667 cls : 'input-group-addon',
11674 if (align ==='left' && this.fieldLabel.length) {
11679 cls : 'control-label',
11680 html : this.fieldLabel
11691 if(this.labelWidth > 12){
11692 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11695 if(this.labelWidth < 13 && this.labelmd == 0){
11696 this.labelmd = this.labelWidth;
11699 if(this.labellg > 0){
11700 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11701 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11704 if(this.labelmd > 0){
11705 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11706 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11709 if(this.labelsm > 0){
11710 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11711 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11714 if(this.labelxs > 0){
11715 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11716 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11719 } else if ( this.fieldLabel.length) {
11724 //cls : 'input-group-addon',
11725 html : this.fieldLabel
11743 if (this.disabled) {
11744 input.disabled=true;
11751 * return the real textarea element.
11753 inputEl: function ()
11755 return this.el.select('textarea.form-control',true).first();
11759 * Clear any invalid styles/messages for this field
11761 clearInvalid : function()
11764 if(!this.el || this.preventMark){ // not rendered
11768 var label = this.el.select('label', true).first();
11769 var icon = this.el.select('i.fa-star', true).first();
11774 this.el.removeClass( this.validClass);
11775 this.inputEl().removeClass('is-invalid');
11777 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11779 var feedback = this.el.select('.form-control-feedback', true).first();
11782 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11787 this.fireEvent('valid', this);
11791 * Mark this field as valid
11793 markValid : function()
11795 if(!this.el || this.preventMark){ // not rendered
11799 this.el.removeClass([this.invalidClass, this.validClass]);
11800 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11802 var feedback = this.el.select('.form-control-feedback', true).first();
11805 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11808 if(this.disabled || this.allowBlank){
11812 var label = this.el.select('label', true).first();
11813 var icon = this.el.select('i.fa-star', true).first();
11818 if (Roo.bootstrap.version == 3) {
11819 this.el.addClass(this.validClass);
11821 this.inputEl().addClass('is-valid');
11825 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11827 var feedback = this.el.select('.form-control-feedback', true).first();
11830 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11831 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11836 this.fireEvent('valid', this);
11840 * Mark this field as invalid
11841 * @param {String} msg The validation message
11843 markInvalid : function(msg)
11845 if(!this.el || this.preventMark){ // not rendered
11849 this.el.removeClass([this.invalidClass, this.validClass]);
11850 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11852 var feedback = this.el.select('.form-control-feedback', true).first();
11855 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11858 if(this.disabled || this.allowBlank){
11862 var label = this.el.select('label', true).first();
11863 var icon = this.el.select('i.fa-star', true).first();
11865 if(!this.getValue().length && label && !icon){
11866 this.el.createChild({
11868 cls : 'text-danger fa fa-lg fa-star',
11869 tooltip : 'This field is required',
11870 style : 'margin-right:5px;'
11874 if (Roo.bootstrap.version == 3) {
11875 this.el.addClass(this.invalidClass);
11877 this.inputEl().addClass('is-invalid');
11880 // fixme ... this may be depricated need to test..
11881 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11883 var feedback = this.el.select('.form-control-feedback', true).first();
11886 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11888 if(this.getValue().length || this.forceFeedback){
11889 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11896 this.fireEvent('invalid', this, msg);
11904 * trigger field - base class for combo..
11909 * @class Roo.bootstrap.TriggerField
11910 * @extends Roo.bootstrap.Input
11911 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11912 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11913 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11914 * for which you can provide a custom implementation. For example:
11916 var trigger = new Roo.bootstrap.TriggerField();
11917 trigger.onTriggerClick = myTriggerFn;
11918 trigger.applyTo('my-field');
11921 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11922 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11923 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11924 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11925 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11928 * Create a new TriggerField.
11929 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11930 * to the base TextField)
11932 Roo.bootstrap.TriggerField = function(config){
11933 this.mimicing = false;
11934 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11937 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11939 * @cfg {String} triggerClass A CSS class to apply to the trigger
11942 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11947 * @cfg {Boolean} removable (true|false) special filter default false
11951 /** @cfg {Boolean} grow @hide */
11952 /** @cfg {Number} growMin @hide */
11953 /** @cfg {Number} growMax @hide */
11959 autoSize: Roo.emptyFn,
11963 deferHeight : true,
11966 actionMode : 'wrap',
11971 getAutoCreate : function(){
11973 var align = this.labelAlign || this.parentLabelAlign();
11978 cls: 'form-group' //input-group
11985 type : this.inputType,
11986 cls : 'form-control',
11987 autocomplete: 'new-password',
11988 placeholder : this.placeholder || ''
11992 input.name = this.name;
11995 input.cls += ' input-' + this.size;
11998 if (this.disabled) {
11999 input.disabled=true;
12002 var inputblock = input;
12004 if(this.hasFeedback && !this.allowBlank){
12008 cls: 'glyphicon form-control-feedback'
12011 if(this.removable && !this.editable ){
12013 cls : 'has-feedback',
12019 cls : 'roo-combo-removable-btn close'
12026 cls : 'has-feedback',
12035 if(this.removable && !this.editable ){
12037 cls : 'roo-removable',
12043 cls : 'roo-combo-removable-btn close'
12050 if (this.before || this.after) {
12053 cls : 'input-group',
12057 inputblock.cn.push({
12059 cls : 'input-group-addon input-group-prepend input-group-text',
12064 inputblock.cn.push(input);
12066 if(this.hasFeedback && !this.allowBlank){
12067 inputblock.cls += ' has-feedback';
12068 inputblock.cn.push(feedback);
12072 inputblock.cn.push({
12074 cls : 'input-group-addon input-group-append input-group-text',
12083 var ibwrap = inputblock;
12088 cls: 'roo-select2-choices',
12092 cls: 'roo-select2-search-field',
12104 cls: 'roo-select2-container input-group',
12109 cls: 'form-hidden-field'
12115 if(!this.multiple && this.showToggleBtn){
12121 if (this.caret != false) {
12124 cls: 'fa fa-' + this.caret
12131 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12133 Roo.bootstrap.version == 3 ? caret : '',
12136 cls: 'combobox-clear',
12150 combobox.cls += ' roo-select2-container-multi';
12154 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12155 tooltip : 'This field is required'
12157 if (Roo.bootstrap.version == 4) {
12160 style : 'display:none'
12165 if (align ==='left' && this.fieldLabel.length) {
12167 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12174 cls : 'control-label',
12175 html : this.fieldLabel
12187 var labelCfg = cfg.cn[1];
12188 var contentCfg = cfg.cn[2];
12190 if(this.indicatorpos == 'right'){
12195 cls : 'control-label',
12199 html : this.fieldLabel
12213 labelCfg = cfg.cn[0];
12214 contentCfg = cfg.cn[1];
12217 if(this.labelWidth > 12){
12218 labelCfg.style = "width: " + this.labelWidth + 'px';
12221 if(this.labelWidth < 13 && this.labelmd == 0){
12222 this.labelmd = this.labelWidth;
12225 if(this.labellg > 0){
12226 labelCfg.cls += ' col-lg-' + this.labellg;
12227 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12230 if(this.labelmd > 0){
12231 labelCfg.cls += ' col-md-' + this.labelmd;
12232 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12235 if(this.labelsm > 0){
12236 labelCfg.cls += ' col-sm-' + this.labelsm;
12237 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12240 if(this.labelxs > 0){
12241 labelCfg.cls += ' col-xs-' + this.labelxs;
12242 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12245 } else if ( this.fieldLabel.length) {
12246 // Roo.log(" label");
12251 //cls : 'input-group-addon',
12252 html : this.fieldLabel
12260 if(this.indicatorpos == 'right'){
12268 html : this.fieldLabel
12282 // Roo.log(" no label && no align");
12289 ['xs','sm','md','lg'].map(function(size){
12290 if (settings[size]) {
12291 cfg.cls += ' col-' + size + '-' + settings[size];
12302 onResize : function(w, h){
12303 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12304 // if(typeof w == 'number'){
12305 // var x = w - this.trigger.getWidth();
12306 // this.inputEl().setWidth(this.adjustWidth('input', x));
12307 // this.trigger.setStyle('left', x+'px');
12312 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12315 getResizeEl : function(){
12316 return this.inputEl();
12320 getPositionEl : function(){
12321 return this.inputEl();
12325 alignErrorIcon : function(){
12326 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12330 initEvents : function(){
12334 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12335 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12336 if(!this.multiple && this.showToggleBtn){
12337 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12338 if(this.hideTrigger){
12339 this.trigger.setDisplayed(false);
12341 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12345 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12348 if(this.removable && !this.editable && !this.tickable){
12349 var close = this.closeTriggerEl();
12352 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12353 close.on('click', this.removeBtnClick, this, close);
12357 //this.trigger.addClassOnOver('x-form-trigger-over');
12358 //this.trigger.addClassOnClick('x-form-trigger-click');
12361 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12365 closeTriggerEl : function()
12367 var close = this.el.select('.roo-combo-removable-btn', true).first();
12368 return close ? close : false;
12371 removeBtnClick : function(e, h, el)
12373 e.preventDefault();
12375 if(this.fireEvent("remove", this) !== false){
12377 this.fireEvent("afterremove", this)
12381 createList : function()
12383 this.list = Roo.get(document.body).createChild({
12384 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12385 cls: 'typeahead typeahead-long dropdown-menu shadow',
12386 style: 'display:none'
12389 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12394 initTrigger : function(){
12399 onDestroy : function(){
12401 this.trigger.removeAllListeners();
12402 // this.trigger.remove();
12405 // this.wrap.remove();
12407 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12411 onFocus : function(){
12412 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12414 if(!this.mimicing){
12415 this.wrap.addClass('x-trigger-wrap-focus');
12416 this.mimicing = true;
12417 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12418 if(this.monitorTab){
12419 this.el.on("keydown", this.checkTab, this);
12426 checkTab : function(e){
12427 if(e.getKey() == e.TAB){
12428 this.triggerBlur();
12433 onBlur : function(){
12438 mimicBlur : function(e, t){
12440 if(!this.wrap.contains(t) && this.validateBlur()){
12441 this.triggerBlur();
12447 triggerBlur : function(){
12448 this.mimicing = false;
12449 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12450 if(this.monitorTab){
12451 this.el.un("keydown", this.checkTab, this);
12453 //this.wrap.removeClass('x-trigger-wrap-focus');
12454 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12458 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12459 validateBlur : function(e, t){
12464 onDisable : function(){
12465 this.inputEl().dom.disabled = true;
12466 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12468 // this.wrap.addClass('x-item-disabled');
12473 onEnable : function(){
12474 this.inputEl().dom.disabled = false;
12475 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12477 // this.el.removeClass('x-item-disabled');
12482 onShow : function(){
12483 var ae = this.getActionEl();
12486 ae.dom.style.display = '';
12487 ae.dom.style.visibility = 'visible';
12493 onHide : function(){
12494 var ae = this.getActionEl();
12495 ae.dom.style.display = 'none';
12499 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12500 * by an implementing function.
12502 * @param {EventObject} e
12504 onTriggerClick : Roo.emptyFn
12512 * @class Roo.bootstrap.CardUploader
12513 * @extends Roo.bootstrap.Button
12514 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12515 * @cfg {Number} errorTimeout default 3000
12516 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12517 * @cfg {Array} html The button text.
12521 * Create a new CardUploader
12522 * @param {Object} config The config object
12525 Roo.bootstrap.CardUploader = function(config){
12529 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12532 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12540 * When a image is clicked on - and needs to display a slideshow or similar..
12541 * @param {Roo.bootstrap.Card} this
12542 * @param {Object} The image information data
12548 * When a the download link is clicked
12549 * @param {Roo.bootstrap.Card} this
12550 * @param {Object} The image information data contains
12557 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12560 errorTimeout : 3000,
12564 fileCollection : false,
12567 getAutoCreate : function()
12571 cls :'form-group' ,
12576 //cls : 'input-group-addon',
12577 html : this.fieldLabel
12585 value : this.value,
12586 cls : 'd-none form-control'
12591 multiple : 'multiple',
12593 cls : 'd-none roo-card-upload-selector'
12597 cls : 'roo-card-uploader-button-container w-100 mb-2'
12600 cls : 'card-columns roo-card-uploader-container'
12610 getChildContainer : function() /// what children are added to.
12612 return this.containerEl;
12615 getButtonContainer : function() /// what children are added to.
12617 return this.el.select(".roo-card-uploader-button-container").first();
12620 initEvents : function()
12623 Roo.bootstrap.Input.prototype.initEvents.call(this);
12627 xns: Roo.bootstrap,
12630 container_method : 'getButtonContainer' ,
12631 html : this.html, // fix changable?
12634 'click' : function(btn, e) {
12643 this.urlAPI = (window.createObjectURL && window) ||
12644 (window.URL && URL.revokeObjectURL && URL) ||
12645 (window.webkitURL && webkitURL);
12650 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12652 this.selectorEl.on('change', this.onFileSelected, this);
12655 this.images.forEach(function(img) {
12658 this.images = false;
12660 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12666 onClick : function(e)
12668 e.preventDefault();
12670 this.selectorEl.dom.click();
12674 onFileSelected : function(e)
12676 e.preventDefault();
12678 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12682 Roo.each(this.selectorEl.dom.files, function(file){
12683 this.addFile(file);
12692 addFile : function(file)
12695 if(typeof(file) === 'string'){
12696 throw "Add file by name?"; // should not happen
12700 if(!file || !this.urlAPI){
12710 var url = _this.urlAPI.createObjectURL( file);
12713 id : Roo.bootstrap.CardUploader.ID--,
12714 is_uploaded : false,
12718 mimetype : file.type,
12726 * addCard - add an Attachment to the uploader
12727 * @param data - the data about the image to upload
12731 title : "Title of file",
12732 is_uploaded : false,
12733 src : "http://.....",
12734 srcfile : { the File upload object },
12735 mimetype : file.type,
12738 .. any other data...
12744 addCard : function (data)
12746 // hidden input element?
12747 // if the file is not an image...
12748 //then we need to use something other that and header_image
12753 xns : Roo.bootstrap,
12754 xtype : 'CardFooter',
12757 xns : Roo.bootstrap,
12763 xns : Roo.bootstrap,
12765 html : String.format("<small>{0}</small>", data.title),
12766 cls : 'col-10 text-left',
12771 click : function() {
12773 t.fireEvent( "download", t, data );
12779 xns : Roo.bootstrap,
12781 style: 'max-height: 28px; ',
12787 click : function() {
12788 t.removeCard(data.id)
12800 var cn = this.addxtype(
12803 xns : Roo.bootstrap,
12806 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12807 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12808 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12813 initEvents : function() {
12814 Roo.bootstrap.Card.prototype.initEvents.call(this);
12816 this.imgEl = this.el.select('.card-img-top').first();
12818 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12819 this.imgEl.set({ 'pointer' : 'cursor' });
12822 this.getCardFooter().addClass('p-1');
12829 // dont' really need ot update items.
12830 // this.items.push(cn);
12831 this.fileCollection.add(cn);
12833 if (!data.srcfile) {
12834 this.updateInput();
12839 var reader = new FileReader();
12840 reader.addEventListener("load", function() {
12841 data.srcdata = reader.result;
12844 reader.readAsDataURL(data.srcfile);
12849 removeCard : function(id)
12852 var card = this.fileCollection.get(id);
12853 card.data.is_deleted = 1;
12854 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12855 //this.fileCollection.remove(card);
12856 //this.items = this.items.filter(function(e) { return e != card });
12857 // dont' really need ot update items.
12858 card.el.dom.parentNode.removeChild(card.el.dom);
12859 this.updateInput();
12865 this.fileCollection.each(function(card) {
12866 if (card.el.dom && card.el.dom.parentNode) {
12867 card.el.dom.parentNode.removeChild(card.el.dom);
12870 this.fileCollection.clear();
12871 this.updateInput();
12874 updateInput : function()
12877 this.fileCollection.each(function(e) {
12881 this.inputEl().dom.value = JSON.stringify(data);
12891 Roo.bootstrap.CardUploader.ID = -1;/*
12893 * Ext JS Library 1.1.1
12894 * Copyright(c) 2006-2007, Ext JS, LLC.
12896 * Originally Released Under LGPL - original licence link has changed is not relivant.
12899 * <script type="text/javascript">
12904 * @class Roo.data.SortTypes
12906 * Defines the default sorting (casting?) comparison functions used when sorting data.
12908 Roo.data.SortTypes = {
12910 * Default sort that does nothing
12911 * @param {Mixed} s The value being converted
12912 * @return {Mixed} The comparison value
12914 none : function(s){
12919 * The regular expression used to strip tags
12923 stripTagsRE : /<\/?[^>]+>/gi,
12926 * Strips all HTML tags to sort on text only
12927 * @param {Mixed} s The value being converted
12928 * @return {String} The comparison value
12930 asText : function(s){
12931 return String(s).replace(this.stripTagsRE, "");
12935 * Strips all HTML tags to sort on text only - Case insensitive
12936 * @param {Mixed} s The value being converted
12937 * @return {String} The comparison value
12939 asUCText : function(s){
12940 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12944 * Case insensitive string
12945 * @param {Mixed} s The value being converted
12946 * @return {String} The comparison value
12948 asUCString : function(s) {
12949 return String(s).toUpperCase();
12954 * @param {Mixed} s The value being converted
12955 * @return {Number} The comparison value
12957 asDate : function(s) {
12961 if(s instanceof Date){
12962 return s.getTime();
12964 return Date.parse(String(s));
12969 * @param {Mixed} s The value being converted
12970 * @return {Float} The comparison value
12972 asFloat : function(s) {
12973 var val = parseFloat(String(s).replace(/,/g, ""));
12982 * @param {Mixed} s The value being converted
12983 * @return {Number} The comparison value
12985 asInt : function(s) {
12986 var val = parseInt(String(s).replace(/,/g, ""));
12994 * Ext JS Library 1.1.1
12995 * Copyright(c) 2006-2007, Ext JS, LLC.
12997 * Originally Released Under LGPL - original licence link has changed is not relivant.
13000 * <script type="text/javascript">
13004 * @class Roo.data.Record
13005 * Instances of this class encapsulate both record <em>definition</em> information, and record
13006 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13007 * to access Records cached in an {@link Roo.data.Store} object.<br>
13009 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13010 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13013 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13015 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13016 * {@link #create}. The parameters are the same.
13017 * @param {Array} data An associative Array of data values keyed by the field name.
13018 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13019 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13020 * not specified an integer id is generated.
13022 Roo.data.Record = function(data, id){
13023 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13028 * Generate a constructor for a specific record layout.
13029 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13030 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13031 * Each field definition object may contain the following properties: <ul>
13032 * <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,
13033 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13034 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13035 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13036 * is being used, then this is a string containing the javascript expression to reference the data relative to
13037 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13038 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13039 * this may be omitted.</p></li>
13040 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13041 * <ul><li>auto (Default, implies no conversion)</li>
13046 * <li>date</li></ul></p></li>
13047 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13048 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13049 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13050 * by the Reader into an object that will be stored in the Record. It is passed the
13051 * following parameters:<ul>
13052 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13054 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13056 * <br>usage:<br><pre><code>
13057 var TopicRecord = Roo.data.Record.create(
13058 {name: 'title', mapping: 'topic_title'},
13059 {name: 'author', mapping: 'username'},
13060 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13061 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13062 {name: 'lastPoster', mapping: 'user2'},
13063 {name: 'excerpt', mapping: 'post_text'}
13066 var myNewRecord = new TopicRecord({
13067 title: 'Do my job please',
13070 lastPost: new Date(),
13071 lastPoster: 'Animal',
13072 excerpt: 'No way dude!'
13074 myStore.add(myNewRecord);
13079 Roo.data.Record.create = function(o){
13080 var f = function(){
13081 f.superclass.constructor.apply(this, arguments);
13083 Roo.extend(f, Roo.data.Record);
13084 var p = f.prototype;
13085 p.fields = new Roo.util.MixedCollection(false, function(field){
13088 for(var i = 0, len = o.length; i < len; i++){
13089 p.fields.add(new Roo.data.Field(o[i]));
13091 f.getField = function(name){
13092 return p.fields.get(name);
13097 Roo.data.Record.AUTO_ID = 1000;
13098 Roo.data.Record.EDIT = 'edit';
13099 Roo.data.Record.REJECT = 'reject';
13100 Roo.data.Record.COMMIT = 'commit';
13102 Roo.data.Record.prototype = {
13104 * Readonly flag - true if this record has been modified.
13113 join : function(store){
13114 this.store = store;
13118 * Set the named field to the specified value.
13119 * @param {String} name The name of the field to set.
13120 * @param {Object} value The value to set the field to.
13122 set : function(name, value){
13123 if(this.data[name] == value){
13127 if(!this.modified){
13128 this.modified = {};
13130 if(typeof this.modified[name] == 'undefined'){
13131 this.modified[name] = this.data[name];
13133 this.data[name] = value;
13134 if(!this.editing && this.store){
13135 this.store.afterEdit(this);
13140 * Get the value of the named field.
13141 * @param {String} name The name of the field to get the value of.
13142 * @return {Object} The value of the field.
13144 get : function(name){
13145 return this.data[name];
13149 beginEdit : function(){
13150 this.editing = true;
13151 this.modified = {};
13155 cancelEdit : function(){
13156 this.editing = false;
13157 delete this.modified;
13161 endEdit : function(){
13162 this.editing = false;
13163 if(this.dirty && this.store){
13164 this.store.afterEdit(this);
13169 * Usually called by the {@link Roo.data.Store} which owns the Record.
13170 * Rejects all changes made to the Record since either creation, or the last commit operation.
13171 * Modified fields are reverted to their original values.
13173 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13174 * of reject operations.
13176 reject : function(){
13177 var m = this.modified;
13179 if(typeof m[n] != "function"){
13180 this.data[n] = m[n];
13183 this.dirty = false;
13184 delete this.modified;
13185 this.editing = false;
13187 this.store.afterReject(this);
13192 * Usually called by the {@link Roo.data.Store} which owns the Record.
13193 * Commits all changes made to the Record since either creation, or the last commit operation.
13195 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13196 * of commit operations.
13198 commit : function(){
13199 this.dirty = false;
13200 delete this.modified;
13201 this.editing = false;
13203 this.store.afterCommit(this);
13208 hasError : function(){
13209 return this.error != null;
13213 clearError : function(){
13218 * Creates a copy of this record.
13219 * @param {String} id (optional) A new record id if you don't want to use this record's id
13222 copy : function(newId) {
13223 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13227 * Ext JS Library 1.1.1
13228 * Copyright(c) 2006-2007, Ext JS, LLC.
13230 * Originally Released Under LGPL - original licence link has changed is not relivant.
13233 * <script type="text/javascript">
13239 * @class Roo.data.Store
13240 * @extends Roo.util.Observable
13241 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13242 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13244 * 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
13245 * has no knowledge of the format of the data returned by the Proxy.<br>
13247 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13248 * instances from the data object. These records are cached and made available through accessor functions.
13250 * Creates a new Store.
13251 * @param {Object} config A config object containing the objects needed for the Store to access data,
13252 * and read the data into Records.
13254 Roo.data.Store = function(config){
13255 this.data = new Roo.util.MixedCollection(false);
13256 this.data.getKey = function(o){
13259 this.baseParams = {};
13261 this.paramNames = {
13266 "multisort" : "_multisort"
13269 if(config && config.data){
13270 this.inlineData = config.data;
13271 delete config.data;
13274 Roo.apply(this, config);
13276 if(this.reader){ // reader passed
13277 this.reader = Roo.factory(this.reader, Roo.data);
13278 this.reader.xmodule = this.xmodule || false;
13279 if(!this.recordType){
13280 this.recordType = this.reader.recordType;
13282 if(this.reader.onMetaChange){
13283 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13287 if(this.recordType){
13288 this.fields = this.recordType.prototype.fields;
13290 this.modified = [];
13294 * @event datachanged
13295 * Fires when the data cache has changed, and a widget which is using this Store
13296 * as a Record cache should refresh its view.
13297 * @param {Store} this
13299 datachanged : true,
13301 * @event metachange
13302 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13303 * @param {Store} this
13304 * @param {Object} meta The JSON metadata
13309 * Fires when Records have been added to the Store
13310 * @param {Store} this
13311 * @param {Roo.data.Record[]} records The array of Records added
13312 * @param {Number} index The index at which the record(s) were added
13317 * Fires when a Record has been removed from the Store
13318 * @param {Store} this
13319 * @param {Roo.data.Record} record The Record that was removed
13320 * @param {Number} index The index at which the record was removed
13325 * Fires when a Record has been updated
13326 * @param {Store} this
13327 * @param {Roo.data.Record} record The Record that was updated
13328 * @param {String} operation The update operation being performed. Value may be one of:
13330 Roo.data.Record.EDIT
13331 Roo.data.Record.REJECT
13332 Roo.data.Record.COMMIT
13338 * Fires when the data cache has been cleared.
13339 * @param {Store} this
13343 * @event beforeload
13344 * Fires before a request is made for a new data object. If the beforeload handler returns false
13345 * the load action will be canceled.
13346 * @param {Store} this
13347 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13351 * @event beforeloadadd
13352 * Fires after a new set of Records has been loaded.
13353 * @param {Store} this
13354 * @param {Roo.data.Record[]} records The Records that were loaded
13355 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13357 beforeloadadd : true,
13360 * Fires after a new set of Records has been loaded, before they are added to the store.
13361 * @param {Store} this
13362 * @param {Roo.data.Record[]} records The Records that were loaded
13363 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13364 * @params {Object} return from reader
13368 * @event loadexception
13369 * Fires if an exception occurs in the Proxy during loading.
13370 * Called with the signature of the Proxy's "loadexception" event.
13371 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13374 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13375 * @param {Object} load options
13376 * @param {Object} jsonData from your request (normally this contains the Exception)
13378 loadexception : true
13382 this.proxy = Roo.factory(this.proxy, Roo.data);
13383 this.proxy.xmodule = this.xmodule || false;
13384 this.relayEvents(this.proxy, ["loadexception"]);
13386 this.sortToggle = {};
13387 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13389 Roo.data.Store.superclass.constructor.call(this);
13391 if(this.inlineData){
13392 this.loadData(this.inlineData);
13393 delete this.inlineData;
13397 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13399 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13400 * without a remote query - used by combo/forms at present.
13404 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13407 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13410 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13411 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13414 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13415 * on any HTTP request
13418 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13421 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13425 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13426 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13428 remoteSort : false,
13431 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13432 * loaded or when a record is removed. (defaults to false).
13434 pruneModifiedRecords : false,
13437 lastOptions : null,
13440 * Add Records to the Store and fires the add event.
13441 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13443 add : function(records){
13444 records = [].concat(records);
13445 for(var i = 0, len = records.length; i < len; i++){
13446 records[i].join(this);
13448 var index = this.data.length;
13449 this.data.addAll(records);
13450 this.fireEvent("add", this, records, index);
13454 * Remove a Record from the Store and fires the remove event.
13455 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13457 remove : function(record){
13458 var index = this.data.indexOf(record);
13459 this.data.removeAt(index);
13461 if(this.pruneModifiedRecords){
13462 this.modified.remove(record);
13464 this.fireEvent("remove", this, record, index);
13468 * Remove all Records from the Store and fires the clear event.
13470 removeAll : function(){
13472 if(this.pruneModifiedRecords){
13473 this.modified = [];
13475 this.fireEvent("clear", this);
13479 * Inserts Records to the Store at the given index and fires the add event.
13480 * @param {Number} index The start index at which to insert the passed Records.
13481 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13483 insert : function(index, records){
13484 records = [].concat(records);
13485 for(var i = 0, len = records.length; i < len; i++){
13486 this.data.insert(index, records[i]);
13487 records[i].join(this);
13489 this.fireEvent("add", this, records, index);
13493 * Get the index within the cache of the passed Record.
13494 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13495 * @return {Number} The index of the passed Record. Returns -1 if not found.
13497 indexOf : function(record){
13498 return this.data.indexOf(record);
13502 * Get the index within the cache of the Record with the passed id.
13503 * @param {String} id The id of the Record to find.
13504 * @return {Number} The index of the Record. Returns -1 if not found.
13506 indexOfId : function(id){
13507 return this.data.indexOfKey(id);
13511 * Get the Record with the specified id.
13512 * @param {String} id The id of the Record to find.
13513 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13515 getById : function(id){
13516 return this.data.key(id);
13520 * Get the Record at the specified index.
13521 * @param {Number} index The index of the Record to find.
13522 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13524 getAt : function(index){
13525 return this.data.itemAt(index);
13529 * Returns a range of Records between specified indices.
13530 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13531 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13532 * @return {Roo.data.Record[]} An array of Records
13534 getRange : function(start, end){
13535 return this.data.getRange(start, end);
13539 storeOptions : function(o){
13540 o = Roo.apply({}, o);
13543 this.lastOptions = o;
13547 * Loads the Record cache from the configured Proxy using the configured Reader.
13549 * If using remote paging, then the first load call must specify the <em>start</em>
13550 * and <em>limit</em> properties in the options.params property to establish the initial
13551 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13553 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13554 * and this call will return before the new data has been loaded. Perform any post-processing
13555 * in a callback function, or in a "load" event handler.</strong>
13557 * @param {Object} options An object containing properties which control loading options:<ul>
13558 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13559 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13560 * passed the following arguments:<ul>
13561 * <li>r : Roo.data.Record[]</li>
13562 * <li>options: Options object from the load call</li>
13563 * <li>success: Boolean success indicator</li></ul></li>
13564 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13565 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13568 load : function(options){
13569 options = options || {};
13570 if(this.fireEvent("beforeload", this, options) !== false){
13571 this.storeOptions(options);
13572 var p = Roo.apply(options.params || {}, this.baseParams);
13573 // if meta was not loaded from remote source.. try requesting it.
13574 if (!this.reader.metaFromRemote) {
13575 p._requestMeta = 1;
13577 if(this.sortInfo && this.remoteSort){
13578 var pn = this.paramNames;
13579 p[pn["sort"]] = this.sortInfo.field;
13580 p[pn["dir"]] = this.sortInfo.direction;
13582 if (this.multiSort) {
13583 var pn = this.paramNames;
13584 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13587 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13592 * Reloads the Record cache from the configured Proxy using the configured Reader and
13593 * the options from the last load operation performed.
13594 * @param {Object} options (optional) An object containing properties which may override the options
13595 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13596 * the most recently used options are reused).
13598 reload : function(options){
13599 this.load(Roo.applyIf(options||{}, this.lastOptions));
13603 // Called as a callback by the Reader during a load operation.
13604 loadRecords : function(o, options, success){
13605 if(!o || success === false){
13606 if(success !== false){
13607 this.fireEvent("load", this, [], options, o);
13609 if(options.callback){
13610 options.callback.call(options.scope || this, [], options, false);
13614 // if data returned failure - throw an exception.
13615 if (o.success === false) {
13616 // show a message if no listener is registered.
13617 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13618 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13620 // loadmask wil be hooked into this..
13621 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13624 var r = o.records, t = o.totalRecords || r.length;
13626 this.fireEvent("beforeloadadd", this, r, options, o);
13628 if(!options || options.add !== true){
13629 if(this.pruneModifiedRecords){
13630 this.modified = [];
13632 for(var i = 0, len = r.length; i < len; i++){
13636 this.data = this.snapshot;
13637 delete this.snapshot;
13640 this.data.addAll(r);
13641 this.totalLength = t;
13643 this.fireEvent("datachanged", this);
13645 this.totalLength = Math.max(t, this.data.length+r.length);
13649 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13651 var e = new Roo.data.Record({});
13653 e.set(this.parent.displayField, this.parent.emptyTitle);
13654 e.set(this.parent.valueField, '');
13659 this.fireEvent("load", this, r, options, o);
13660 if(options.callback){
13661 options.callback.call(options.scope || this, r, options, true);
13667 * Loads data from a passed data block. A Reader which understands the format of the data
13668 * must have been configured in the constructor.
13669 * @param {Object} data The data block from which to read the Records. The format of the data expected
13670 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13671 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13673 loadData : function(o, append){
13674 var r = this.reader.readRecords(o);
13675 this.loadRecords(r, {add: append}, true);
13679 * using 'cn' the nested child reader read the child array into it's child stores.
13680 * @param {Object} rec The record with a 'children array
13682 loadDataFromChildren : function(rec)
13684 this.loadData(this.reader.toLoadData(rec));
13689 * Gets the number of cached records.
13691 * <em>If using paging, this may not be the total size of the dataset. If the data object
13692 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13693 * the data set size</em>
13695 getCount : function(){
13696 return this.data.length || 0;
13700 * Gets the total number of records in the dataset as returned by the server.
13702 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13703 * the dataset size</em>
13705 getTotalCount : function(){
13706 return this.totalLength || 0;
13710 * Returns the sort state of the Store as an object with two properties:
13712 field {String} The name of the field by which the Records are sorted
13713 direction {String} The sort order, "ASC" or "DESC"
13716 getSortState : function(){
13717 return this.sortInfo;
13721 applySort : function(){
13722 if(this.sortInfo && !this.remoteSort){
13723 var s = this.sortInfo, f = s.field;
13724 var st = this.fields.get(f).sortType;
13725 var fn = function(r1, r2){
13726 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13727 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13729 this.data.sort(s.direction, fn);
13730 if(this.snapshot && this.snapshot != this.data){
13731 this.snapshot.sort(s.direction, fn);
13737 * Sets the default sort column and order to be used by the next load operation.
13738 * @param {String} fieldName The name of the field to sort by.
13739 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13741 setDefaultSort : function(field, dir){
13742 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13746 * Sort the Records.
13747 * If remote sorting is used, the sort is performed on the server, and the cache is
13748 * reloaded. If local sorting is used, the cache is sorted internally.
13749 * @param {String} fieldName The name of the field to sort by.
13750 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13752 sort : function(fieldName, dir){
13753 var f = this.fields.get(fieldName);
13755 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13757 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13758 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13763 this.sortToggle[f.name] = dir;
13764 this.sortInfo = {field: f.name, direction: dir};
13765 if(!this.remoteSort){
13767 this.fireEvent("datachanged", this);
13769 this.load(this.lastOptions);
13774 * Calls the specified function for each of the Records in the cache.
13775 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13776 * Returning <em>false</em> aborts and exits the iteration.
13777 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13779 each : function(fn, scope){
13780 this.data.each(fn, scope);
13784 * Gets all records modified since the last commit. Modified records are persisted across load operations
13785 * (e.g., during paging).
13786 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13788 getModifiedRecords : function(){
13789 return this.modified;
13793 createFilterFn : function(property, value, anyMatch){
13794 if(!value.exec){ // not a regex
13795 value = String(value);
13796 if(value.length == 0){
13799 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13801 return function(r){
13802 return value.test(r.data[property]);
13807 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13808 * @param {String} property A field on your records
13809 * @param {Number} start The record index to start at (defaults to 0)
13810 * @param {Number} end The last record index to include (defaults to length - 1)
13811 * @return {Number} The sum
13813 sum : function(property, start, end){
13814 var rs = this.data.items, v = 0;
13815 start = start || 0;
13816 end = (end || end === 0) ? end : rs.length-1;
13818 for(var i = start; i <= end; i++){
13819 v += (rs[i].data[property] || 0);
13825 * Filter the records by a specified property.
13826 * @param {String} field A field on your records
13827 * @param {String/RegExp} value Either a string that the field
13828 * should start with or a RegExp to test against the field
13829 * @param {Boolean} anyMatch True to match any part not just the beginning
13831 filter : function(property, value, anyMatch){
13832 var fn = this.createFilterFn(property, value, anyMatch);
13833 return fn ? this.filterBy(fn) : this.clearFilter();
13837 * Filter by a function. The specified function will be called with each
13838 * record in this data source. If the function returns true the record is included,
13839 * otherwise it is filtered.
13840 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13841 * @param {Object} scope (optional) The scope of the function (defaults to this)
13843 filterBy : function(fn, scope){
13844 this.snapshot = this.snapshot || this.data;
13845 this.data = this.queryBy(fn, scope||this);
13846 this.fireEvent("datachanged", this);
13850 * Query the records by a specified property.
13851 * @param {String} field A field on your records
13852 * @param {String/RegExp} value Either a string that the field
13853 * should start with or a RegExp to test against the field
13854 * @param {Boolean} anyMatch True to match any part not just the beginning
13855 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13857 query : function(property, value, anyMatch){
13858 var fn = this.createFilterFn(property, value, anyMatch);
13859 return fn ? this.queryBy(fn) : this.data.clone();
13863 * Query by a function. The specified function will be called with each
13864 * record in this data source. If the function returns true the record is included
13866 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13867 * @param {Object} scope (optional) The scope of the function (defaults to this)
13868 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13870 queryBy : function(fn, scope){
13871 var data = this.snapshot || this.data;
13872 return data.filterBy(fn, scope||this);
13876 * Collects unique values for a particular dataIndex from this store.
13877 * @param {String} dataIndex The property to collect
13878 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13879 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13880 * @return {Array} An array of the unique values
13882 collect : function(dataIndex, allowNull, bypassFilter){
13883 var d = (bypassFilter === true && this.snapshot) ?
13884 this.snapshot.items : this.data.items;
13885 var v, sv, r = [], l = {};
13886 for(var i = 0, len = d.length; i < len; i++){
13887 v = d[i].data[dataIndex];
13889 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13898 * Revert to a view of the Record cache with no filtering applied.
13899 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13901 clearFilter : function(suppressEvent){
13902 if(this.snapshot && this.snapshot != this.data){
13903 this.data = this.snapshot;
13904 delete this.snapshot;
13905 if(suppressEvent !== true){
13906 this.fireEvent("datachanged", this);
13912 afterEdit : function(record){
13913 if(this.modified.indexOf(record) == -1){
13914 this.modified.push(record);
13916 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13920 afterReject : function(record){
13921 this.modified.remove(record);
13922 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13926 afterCommit : function(record){
13927 this.modified.remove(record);
13928 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13932 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13933 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13935 commitChanges : function(){
13936 var m = this.modified.slice(0);
13937 this.modified = [];
13938 for(var i = 0, len = m.length; i < len; i++){
13944 * Cancel outstanding changes on all changed records.
13946 rejectChanges : function(){
13947 var m = this.modified.slice(0);
13948 this.modified = [];
13949 for(var i = 0, len = m.length; i < len; i++){
13954 onMetaChange : function(meta, rtype, o){
13955 this.recordType = rtype;
13956 this.fields = rtype.prototype.fields;
13957 delete this.snapshot;
13958 this.sortInfo = meta.sortInfo || this.sortInfo;
13959 this.modified = [];
13960 this.fireEvent('metachange', this, this.reader.meta);
13963 moveIndex : function(data, type)
13965 var index = this.indexOf(data);
13967 var newIndex = index + type;
13971 this.insert(newIndex, data);
13976 * Ext JS Library 1.1.1
13977 * Copyright(c) 2006-2007, Ext JS, LLC.
13979 * Originally Released Under LGPL - original licence link has changed is not relivant.
13982 * <script type="text/javascript">
13986 * @class Roo.data.SimpleStore
13987 * @extends Roo.data.Store
13988 * Small helper class to make creating Stores from Array data easier.
13989 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13990 * @cfg {Array} fields An array of field definition objects, or field name strings.
13991 * @cfg {Object} an existing reader (eg. copied from another store)
13992 * @cfg {Array} data The multi-dimensional array of data
13994 * @param {Object} config
13996 Roo.data.SimpleStore = function(config)
13998 Roo.data.SimpleStore.superclass.constructor.call(this, {
14000 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14003 Roo.data.Record.create(config.fields)
14005 proxy : new Roo.data.MemoryProxy(config.data)
14009 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14011 * Ext JS Library 1.1.1
14012 * Copyright(c) 2006-2007, Ext JS, LLC.
14014 * Originally Released Under LGPL - original licence link has changed is not relivant.
14017 * <script type="text/javascript">
14022 * @extends Roo.data.Store
14023 * @class Roo.data.JsonStore
14024 * Small helper class to make creating Stores for JSON data easier. <br/>
14026 var store = new Roo.data.JsonStore({
14027 url: 'get-images.php',
14029 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14032 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14033 * JsonReader and HttpProxy (unless inline data is provided).</b>
14034 * @cfg {Array} fields An array of field definition objects, or field name strings.
14036 * @param {Object} config
14038 Roo.data.JsonStore = function(c){
14039 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14040 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14041 reader: new Roo.data.JsonReader(c, c.fields)
14044 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14046 * Ext JS Library 1.1.1
14047 * Copyright(c) 2006-2007, Ext JS, LLC.
14049 * Originally Released Under LGPL - original licence link has changed is not relivant.
14052 * <script type="text/javascript">
14056 Roo.data.Field = function(config){
14057 if(typeof config == "string"){
14058 config = {name: config};
14060 Roo.apply(this, config);
14063 this.type = "auto";
14066 var st = Roo.data.SortTypes;
14067 // named sortTypes are supported, here we look them up
14068 if(typeof this.sortType == "string"){
14069 this.sortType = st[this.sortType];
14072 // set default sortType for strings and dates
14073 if(!this.sortType){
14076 this.sortType = st.asUCString;
14079 this.sortType = st.asDate;
14082 this.sortType = st.none;
14087 var stripRe = /[\$,%]/g;
14089 // prebuilt conversion function for this field, instead of
14090 // switching every time we're reading a value
14092 var cv, dateFormat = this.dateFormat;
14097 cv = function(v){ return v; };
14100 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14104 return v !== undefined && v !== null && v !== '' ?
14105 parseInt(String(v).replace(stripRe, ""), 10) : '';
14110 return v !== undefined && v !== null && v !== '' ?
14111 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14116 cv = function(v){ return v === true || v === "true" || v == 1; };
14123 if(v instanceof Date){
14127 if(dateFormat == "timestamp"){
14128 return new Date(v*1000);
14130 return Date.parseDate(v, dateFormat);
14132 var parsed = Date.parse(v);
14133 return parsed ? new Date(parsed) : null;
14142 Roo.data.Field.prototype = {
14150 * Ext JS Library 1.1.1
14151 * Copyright(c) 2006-2007, Ext JS, LLC.
14153 * Originally Released Under LGPL - original licence link has changed is not relivant.
14156 * <script type="text/javascript">
14159 // Base class for reading structured data from a data source. This class is intended to be
14160 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14163 * @class Roo.data.DataReader
14164 * Base class for reading structured data from a data source. This class is intended to be
14165 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14168 Roo.data.DataReader = function(meta, recordType){
14172 this.recordType = recordType instanceof Array ?
14173 Roo.data.Record.create(recordType) : recordType;
14176 Roo.data.DataReader.prototype = {
14179 readerType : 'Data',
14181 * Create an empty record
14182 * @param {Object} data (optional) - overlay some values
14183 * @return {Roo.data.Record} record created.
14185 newRow : function(d) {
14187 this.recordType.prototype.fields.each(function(c) {
14189 case 'int' : da[c.name] = 0; break;
14190 case 'date' : da[c.name] = new Date(); break;
14191 case 'float' : da[c.name] = 0.0; break;
14192 case 'boolean' : da[c.name] = false; break;
14193 default : da[c.name] = ""; break;
14197 return new this.recordType(Roo.apply(da, d));
14203 * Ext JS Library 1.1.1
14204 * Copyright(c) 2006-2007, Ext JS, LLC.
14206 * Originally Released Under LGPL - original licence link has changed is not relivant.
14209 * <script type="text/javascript">
14213 * @class Roo.data.DataProxy
14214 * @extends Roo.data.Observable
14215 * This class is an abstract base class for implementations which provide retrieval of
14216 * unformatted data objects.<br>
14218 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14219 * (of the appropriate type which knows how to parse the data object) to provide a block of
14220 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14222 * Custom implementations must implement the load method as described in
14223 * {@link Roo.data.HttpProxy#load}.
14225 Roo.data.DataProxy = function(){
14228 * @event beforeload
14229 * Fires before a network request is made to retrieve a data object.
14230 * @param {Object} This DataProxy object.
14231 * @param {Object} params The params parameter to the load function.
14236 * Fires before the load method's callback is called.
14237 * @param {Object} This DataProxy object.
14238 * @param {Object} o The data object.
14239 * @param {Object} arg The callback argument object passed to the load function.
14243 * @event loadexception
14244 * Fires if an Exception occurs during data retrieval.
14245 * @param {Object} This DataProxy object.
14246 * @param {Object} o The data object.
14247 * @param {Object} arg The callback argument object passed to the load function.
14248 * @param {Object} e The Exception.
14250 loadexception : true
14252 Roo.data.DataProxy.superclass.constructor.call(this);
14255 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14258 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14262 * Ext JS Library 1.1.1
14263 * Copyright(c) 2006-2007, Ext JS, LLC.
14265 * Originally Released Under LGPL - original licence link has changed is not relivant.
14268 * <script type="text/javascript">
14271 * @class Roo.data.MemoryProxy
14272 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14273 * to the Reader when its load method is called.
14275 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14277 Roo.data.MemoryProxy = function(data){
14281 Roo.data.MemoryProxy.superclass.constructor.call(this);
14285 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14288 * Load data from the requested source (in this case an in-memory
14289 * data object passed to the constructor), read the data object into
14290 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14291 * process that block using the passed callback.
14292 * @param {Object} params This parameter is not used by the MemoryProxy class.
14293 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14294 * object into a block of Roo.data.Records.
14295 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14296 * The function must be passed <ul>
14297 * <li>The Record block object</li>
14298 * <li>The "arg" argument from the load function</li>
14299 * <li>A boolean success indicator</li>
14301 * @param {Object} scope The scope in which to call the callback
14302 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14304 load : function(params, reader, callback, scope, arg){
14305 params = params || {};
14308 result = reader.readRecords(params.data ? params.data :this.data);
14310 this.fireEvent("loadexception", this, arg, null, e);
14311 callback.call(scope, null, arg, false);
14314 callback.call(scope, result, arg, true);
14318 update : function(params, records){
14323 * Ext JS Library 1.1.1
14324 * Copyright(c) 2006-2007, Ext JS, LLC.
14326 * Originally Released Under LGPL - original licence link has changed is not relivant.
14329 * <script type="text/javascript">
14332 * @class Roo.data.HttpProxy
14333 * @extends Roo.data.DataProxy
14334 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14335 * configured to reference a certain URL.<br><br>
14337 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14338 * from which the running page was served.<br><br>
14340 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14342 * Be aware that to enable the browser to parse an XML document, the server must set
14343 * the Content-Type header in the HTTP response to "text/xml".
14345 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14346 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14347 * will be used to make the request.
14349 Roo.data.HttpProxy = function(conn){
14350 Roo.data.HttpProxy.superclass.constructor.call(this);
14351 // is conn a conn config or a real conn?
14353 this.useAjax = !conn || !conn.events;
14357 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14358 // thse are take from connection...
14361 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14364 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14365 * extra parameters to each request made by this object. (defaults to undefined)
14368 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14369 * to each request made by this object. (defaults to undefined)
14372 * @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)
14375 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14378 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14384 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14388 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14389 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14390 * a finer-grained basis than the DataProxy events.
14392 getConnection : function(){
14393 return this.useAjax ? Roo.Ajax : this.conn;
14397 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14398 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14399 * process that block using the passed callback.
14400 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14401 * for the request to the remote server.
14402 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14403 * object into a block of Roo.data.Records.
14404 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14405 * The function must be passed <ul>
14406 * <li>The Record block object</li>
14407 * <li>The "arg" argument from the load function</li>
14408 * <li>A boolean success indicator</li>
14410 * @param {Object} scope The scope in which to call the callback
14411 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14413 load : function(params, reader, callback, scope, arg){
14414 if(this.fireEvent("beforeload", this, params) !== false){
14416 params : params || {},
14418 callback : callback,
14423 callback : this.loadResponse,
14427 Roo.applyIf(o, this.conn);
14428 if(this.activeRequest){
14429 Roo.Ajax.abort(this.activeRequest);
14431 this.activeRequest = Roo.Ajax.request(o);
14433 this.conn.request(o);
14436 callback.call(scope||this, null, arg, false);
14441 loadResponse : function(o, success, response){
14442 delete this.activeRequest;
14444 this.fireEvent("loadexception", this, o, response);
14445 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14450 result = o.reader.read(response);
14452 this.fireEvent("loadexception", this, o, response, e);
14453 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14457 this.fireEvent("load", this, o, o.request.arg);
14458 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14462 update : function(dataSet){
14467 updateResponse : function(dataSet){
14472 * Ext JS Library 1.1.1
14473 * Copyright(c) 2006-2007, Ext JS, LLC.
14475 * Originally Released Under LGPL - original licence link has changed is not relivant.
14478 * <script type="text/javascript">
14482 * @class Roo.data.ScriptTagProxy
14483 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14484 * other than the originating domain of the running page.<br><br>
14486 * <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
14487 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14489 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14490 * source code that is used as the source inside a <script> tag.<br><br>
14492 * In order for the browser to process the returned data, the server must wrap the data object
14493 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14494 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14495 * depending on whether the callback name was passed:
14498 boolean scriptTag = false;
14499 String cb = request.getParameter("callback");
14502 response.setContentType("text/javascript");
14504 response.setContentType("application/x-json");
14506 Writer out = response.getWriter();
14508 out.write(cb + "(");
14510 out.print(dataBlock.toJsonString());
14517 * @param {Object} config A configuration object.
14519 Roo.data.ScriptTagProxy = function(config){
14520 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14521 Roo.apply(this, config);
14522 this.head = document.getElementsByTagName("head")[0];
14525 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14527 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14529 * @cfg {String} url The URL from which to request the data object.
14532 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14536 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14537 * the server the name of the callback function set up by the load call to process the returned data object.
14538 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14539 * javascript output which calls this named function passing the data object as its only parameter.
14541 callbackParam : "callback",
14543 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14544 * name to the request.
14549 * Load data from the configured URL, read the data object into
14550 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14551 * process that block using the passed callback.
14552 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14553 * for the request to the remote server.
14554 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14555 * object into a block of Roo.data.Records.
14556 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14557 * The function must be passed <ul>
14558 * <li>The Record block object</li>
14559 * <li>The "arg" argument from the load function</li>
14560 * <li>A boolean success indicator</li>
14562 * @param {Object} scope The scope in which to call the callback
14563 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14565 load : function(params, reader, callback, scope, arg){
14566 if(this.fireEvent("beforeload", this, params) !== false){
14568 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14570 var url = this.url;
14571 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14573 url += "&_dc=" + (new Date().getTime());
14575 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14578 cb : "stcCallback"+transId,
14579 scriptId : "stcScript"+transId,
14583 callback : callback,
14589 window[trans.cb] = function(o){
14590 conn.handleResponse(o, trans);
14593 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14595 if(this.autoAbort !== false){
14599 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14601 var script = document.createElement("script");
14602 script.setAttribute("src", url);
14603 script.setAttribute("type", "text/javascript");
14604 script.setAttribute("id", trans.scriptId);
14605 this.head.appendChild(script);
14607 this.trans = trans;
14609 callback.call(scope||this, null, arg, false);
14614 isLoading : function(){
14615 return this.trans ? true : false;
14619 * Abort the current server request.
14621 abort : function(){
14622 if(this.isLoading()){
14623 this.destroyTrans(this.trans);
14628 destroyTrans : function(trans, isLoaded){
14629 this.head.removeChild(document.getElementById(trans.scriptId));
14630 clearTimeout(trans.timeoutId);
14632 window[trans.cb] = undefined;
14634 delete window[trans.cb];
14637 // if hasn't been loaded, wait for load to remove it to prevent script error
14638 window[trans.cb] = function(){
14639 window[trans.cb] = undefined;
14641 delete window[trans.cb];
14648 handleResponse : function(o, trans){
14649 this.trans = false;
14650 this.destroyTrans(trans, true);
14653 result = trans.reader.readRecords(o);
14655 this.fireEvent("loadexception", this, o, trans.arg, e);
14656 trans.callback.call(trans.scope||window, null, trans.arg, false);
14659 this.fireEvent("load", this, o, trans.arg);
14660 trans.callback.call(trans.scope||window, result, trans.arg, true);
14664 handleFailure : function(trans){
14665 this.trans = false;
14666 this.destroyTrans(trans, false);
14667 this.fireEvent("loadexception", this, null, trans.arg);
14668 trans.callback.call(trans.scope||window, null, trans.arg, false);
14672 * Ext JS Library 1.1.1
14673 * Copyright(c) 2006-2007, Ext JS, LLC.
14675 * Originally Released Under LGPL - original licence link has changed is not relivant.
14678 * <script type="text/javascript">
14682 * @class Roo.data.JsonReader
14683 * @extends Roo.data.DataReader
14684 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14685 * based on mappings in a provided Roo.data.Record constructor.
14687 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14688 * in the reply previously.
14693 var RecordDef = Roo.data.Record.create([
14694 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14695 {name: 'occupation'} // This field will use "occupation" as the mapping.
14697 var myReader = new Roo.data.JsonReader({
14698 totalProperty: "results", // The property which contains the total dataset size (optional)
14699 root: "rows", // The property which contains an Array of row objects
14700 id: "id" // The property within each row object that provides an ID for the record (optional)
14704 * This would consume a JSON file like this:
14706 { 'results': 2, 'rows': [
14707 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14708 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14711 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14712 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14713 * paged from the remote server.
14714 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14715 * @cfg {String} root name of the property which contains the Array of row objects.
14716 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14717 * @cfg {Array} fields Array of field definition objects
14719 * Create a new JsonReader
14720 * @param {Object} meta Metadata configuration options
14721 * @param {Object} recordType Either an Array of field definition objects,
14722 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14724 Roo.data.JsonReader = function(meta, recordType){
14727 // set some defaults:
14728 Roo.applyIf(meta, {
14729 totalProperty: 'total',
14730 successProperty : 'success',
14735 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14737 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14739 readerType : 'Json',
14742 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14743 * Used by Store query builder to append _requestMeta to params.
14746 metaFromRemote : false,
14748 * This method is only used by a DataProxy which has retrieved data from a remote server.
14749 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14750 * @return {Object} data A data block which is used by an Roo.data.Store object as
14751 * a cache of Roo.data.Records.
14753 read : function(response){
14754 var json = response.responseText;
14756 var o = /* eval:var:o */ eval("("+json+")");
14758 throw {message: "JsonReader.read: Json object not found"};
14764 this.metaFromRemote = true;
14765 this.meta = o.metaData;
14766 this.recordType = Roo.data.Record.create(o.metaData.fields);
14767 this.onMetaChange(this.meta, this.recordType, o);
14769 return this.readRecords(o);
14772 // private function a store will implement
14773 onMetaChange : function(meta, recordType, o){
14780 simpleAccess: function(obj, subsc) {
14787 getJsonAccessor: function(){
14789 return function(expr) {
14791 return(re.test(expr))
14792 ? new Function("obj", "return obj." + expr)
14797 return Roo.emptyFn;
14802 * Create a data block containing Roo.data.Records from an XML document.
14803 * @param {Object} o An object which contains an Array of row objects in the property specified
14804 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14805 * which contains the total size of the dataset.
14806 * @return {Object} data A data block which is used by an Roo.data.Store object as
14807 * a cache of Roo.data.Records.
14809 readRecords : function(o){
14811 * After any data loads, the raw JSON data is available for further custom processing.
14815 var s = this.meta, Record = this.recordType,
14816 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14818 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14820 if(s.totalProperty) {
14821 this.getTotal = this.getJsonAccessor(s.totalProperty);
14823 if(s.successProperty) {
14824 this.getSuccess = this.getJsonAccessor(s.successProperty);
14826 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14828 var g = this.getJsonAccessor(s.id);
14829 this.getId = function(rec) {
14831 return (r === undefined || r === "") ? null : r;
14834 this.getId = function(){return null;};
14837 for(var jj = 0; jj < fl; jj++){
14839 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14840 this.ef[jj] = this.getJsonAccessor(map);
14844 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14845 if(s.totalProperty){
14846 var vt = parseInt(this.getTotal(o), 10);
14851 if(s.successProperty){
14852 var vs = this.getSuccess(o);
14853 if(vs === false || vs === 'false'){
14858 for(var i = 0; i < c; i++){
14861 var id = this.getId(n);
14862 for(var j = 0; j < fl; j++){
14864 var v = this.ef[j](n);
14866 Roo.log('missing convert for ' + f.name);
14870 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14872 var record = new Record(values, id);
14874 records[i] = record;
14880 totalRecords : totalRecords
14883 // used when loading children.. @see loadDataFromChildren
14884 toLoadData: function(rec)
14886 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14887 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14888 return { data : data, total : data.length };
14893 * Ext JS Library 1.1.1
14894 * Copyright(c) 2006-2007, Ext JS, LLC.
14896 * Originally Released Under LGPL - original licence link has changed is not relivant.
14899 * <script type="text/javascript">
14903 * @class Roo.data.ArrayReader
14904 * @extends Roo.data.DataReader
14905 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14906 * Each element of that Array represents a row of data fields. The
14907 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14908 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14912 var RecordDef = Roo.data.Record.create([
14913 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14914 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14916 var myReader = new Roo.data.ArrayReader({
14917 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14921 * This would consume an Array like this:
14923 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14927 * Create a new JsonReader
14928 * @param {Object} meta Metadata configuration options.
14929 * @param {Object|Array} recordType Either an Array of field definition objects
14931 * @cfg {Array} fields Array of field definition objects
14932 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14933 * as specified to {@link Roo.data.Record#create},
14934 * or an {@link Roo.data.Record} object
14937 * created using {@link Roo.data.Record#create}.
14939 Roo.data.ArrayReader = function(meta, recordType)
14941 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14944 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14947 * Create a data block containing Roo.data.Records from an XML document.
14948 * @param {Object} o An Array of row objects which represents the dataset.
14949 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14950 * a cache of Roo.data.Records.
14952 readRecords : function(o)
14954 var sid = this.meta ? this.meta.id : null;
14955 var recordType = this.recordType, fields = recordType.prototype.fields;
14958 for(var i = 0; i < root.length; i++){
14961 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14962 for(var j = 0, jlen = fields.length; j < jlen; j++){
14963 var f = fields.items[j];
14964 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14965 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14967 values[f.name] = v;
14969 var record = new recordType(values, id);
14971 records[records.length] = record;
14975 totalRecords : records.length
14978 // used when loading children.. @see loadDataFromChildren
14979 toLoadData: function(rec)
14981 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14982 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14993 * @class Roo.bootstrap.ComboBox
14994 * @extends Roo.bootstrap.TriggerField
14995 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14996 * @cfg {Boolean} append (true|false) default false
14997 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14998 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14999 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15000 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15001 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15002 * @cfg {Boolean} animate default true
15003 * @cfg {Boolean} emptyResultText only for touch device
15004 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15005 * @cfg {String} emptyTitle default ''
15006 * @cfg {Number} width fixed with? experimental
15008 * Create a new ComboBox.
15009 * @param {Object} config Configuration options
15011 Roo.bootstrap.ComboBox = function(config){
15012 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15016 * Fires when the dropdown list is expanded
15017 * @param {Roo.bootstrap.ComboBox} combo This combo box
15022 * Fires when the dropdown list is collapsed
15023 * @param {Roo.bootstrap.ComboBox} combo This combo box
15027 * @event beforeselect
15028 * Fires before a list item is selected. Return false to cancel the selection.
15029 * @param {Roo.bootstrap.ComboBox} combo This combo box
15030 * @param {Roo.data.Record} record The data record returned from the underlying store
15031 * @param {Number} index The index of the selected item in the dropdown list
15033 'beforeselect' : true,
15036 * Fires when a list item is selected
15037 * @param {Roo.bootstrap.ComboBox} combo This combo box
15038 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15039 * @param {Number} index The index of the selected item in the dropdown list
15043 * @event beforequery
15044 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15045 * The event object passed has these properties:
15046 * @param {Roo.bootstrap.ComboBox} combo This combo box
15047 * @param {String} query The query
15048 * @param {Boolean} forceAll true to force "all" query
15049 * @param {Boolean} cancel true to cancel the query
15050 * @param {Object} e The query event object
15052 'beforequery': true,
15055 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15056 * @param {Roo.bootstrap.ComboBox} combo This combo box
15061 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15062 * @param {Roo.bootstrap.ComboBox} combo This combo box
15063 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15068 * Fires when the remove value from the combobox array
15069 * @param {Roo.bootstrap.ComboBox} combo This combo box
15073 * @event afterremove
15074 * Fires when the remove value from the combobox array
15075 * @param {Roo.bootstrap.ComboBox} combo This combo box
15077 'afterremove' : true,
15079 * @event specialfilter
15080 * Fires when specialfilter
15081 * @param {Roo.bootstrap.ComboBox} combo This combo box
15083 'specialfilter' : true,
15086 * Fires when tick the element
15087 * @param {Roo.bootstrap.ComboBox} combo This combo box
15091 * @event touchviewdisplay
15092 * Fires when touch view require special display (default is using displayField)
15093 * @param {Roo.bootstrap.ComboBox} combo This combo box
15094 * @param {Object} cfg set html .
15096 'touchviewdisplay' : true
15101 this.tickItems = [];
15103 this.selectedIndex = -1;
15104 if(this.mode == 'local'){
15105 if(config.queryDelay === undefined){
15106 this.queryDelay = 10;
15108 if(config.minChars === undefined){
15114 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15117 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15118 * rendering into an Roo.Editor, defaults to false)
15121 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15122 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15125 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15128 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15129 * the dropdown list (defaults to undefined, with no header element)
15133 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15137 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15139 listWidth: undefined,
15141 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15142 * mode = 'remote' or 'text' if mode = 'local')
15144 displayField: undefined,
15147 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15148 * mode = 'remote' or 'value' if mode = 'local').
15149 * Note: use of a valueField requires the user make a selection
15150 * in order for a value to be mapped.
15152 valueField: undefined,
15154 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15159 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15160 * field's data value (defaults to the underlying DOM element's name)
15162 hiddenName: undefined,
15164 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15168 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15170 selectedClass: 'active',
15173 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15177 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15178 * anchor positions (defaults to 'tl-bl')
15180 listAlign: 'tl-bl?',
15182 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15186 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15187 * query specified by the allQuery config option (defaults to 'query')
15189 triggerAction: 'query',
15191 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15192 * (defaults to 4, does not apply if editable = false)
15196 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15197 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15201 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15202 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15206 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15207 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15211 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15212 * when editable = true (defaults to false)
15214 selectOnFocus:false,
15216 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15218 queryParam: 'query',
15220 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15221 * when mode = 'remote' (defaults to 'Loading...')
15223 loadingText: 'Loading...',
15225 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15229 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15233 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15234 * traditional select (defaults to true)
15238 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15242 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15246 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15247 * listWidth has a higher value)
15251 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15252 * allow the user to set arbitrary text into the field (defaults to false)
15254 forceSelection:false,
15256 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15257 * if typeAhead = true (defaults to 250)
15259 typeAheadDelay : 250,
15261 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15262 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15264 valueNotFoundText : undefined,
15266 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15268 blockFocus : false,
15271 * @cfg {Boolean} disableClear Disable showing of clear button.
15273 disableClear : false,
15275 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15277 alwaysQuery : false,
15280 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15285 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15287 invalidClass : "has-warning",
15290 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15292 validClass : "has-success",
15295 * @cfg {Boolean} specialFilter (true|false) special filter default false
15297 specialFilter : false,
15300 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15302 mobileTouchView : true,
15305 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15307 useNativeIOS : false,
15310 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15312 mobile_restrict_height : false,
15314 ios_options : false,
15326 btnPosition : 'right',
15327 triggerList : true,
15328 showToggleBtn : true,
15330 emptyResultText: 'Empty',
15331 triggerText : 'Select',
15335 // element that contains real text value.. (when hidden is used..)
15337 getAutoCreate : function()
15342 * Render classic select for iso
15345 if(Roo.isIOS && this.useNativeIOS){
15346 cfg = this.getAutoCreateNativeIOS();
15354 if(Roo.isTouch && this.mobileTouchView){
15355 cfg = this.getAutoCreateTouchView();
15362 if(!this.tickable){
15363 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15368 * ComboBox with tickable selections
15371 var align = this.labelAlign || this.parentLabelAlign();
15374 cls : 'form-group roo-combobox-tickable' //input-group
15377 var btn_text_select = '';
15378 var btn_text_done = '';
15379 var btn_text_cancel = '';
15381 if (this.btn_text_show) {
15382 btn_text_select = 'Select';
15383 btn_text_done = 'Done';
15384 btn_text_cancel = 'Cancel';
15389 cls : 'tickable-buttons',
15394 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15395 //html : this.triggerText
15396 html: btn_text_select
15402 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15404 html: btn_text_done
15410 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15412 html: btn_text_cancel
15418 buttons.cn.unshift({
15420 cls: 'roo-select2-search-field-input'
15426 Roo.each(buttons.cn, function(c){
15428 c.cls += ' btn-' + _this.size;
15431 if (_this.disabled) {
15438 style : 'display: contents',
15443 cls: 'form-hidden-field'
15447 cls: 'roo-select2-choices',
15451 cls: 'roo-select2-search-field',
15462 cls: 'roo-select2-container input-group roo-select2-container-multi',
15468 // cls: 'typeahead typeahead-long dropdown-menu',
15469 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15474 if(this.hasFeedback && !this.allowBlank){
15478 cls: 'glyphicon form-control-feedback'
15481 combobox.cn.push(feedback);
15488 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15489 tooltip : 'This field is required'
15491 if (Roo.bootstrap.version == 4) {
15494 style : 'display:none'
15497 if (align ==='left' && this.fieldLabel.length) {
15499 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15506 cls : 'control-label col-form-label',
15507 html : this.fieldLabel
15519 var labelCfg = cfg.cn[1];
15520 var contentCfg = cfg.cn[2];
15523 if(this.indicatorpos == 'right'){
15529 cls : 'control-label col-form-label',
15533 html : this.fieldLabel
15549 labelCfg = cfg.cn[0];
15550 contentCfg = cfg.cn[1];
15554 if(this.labelWidth > 12){
15555 labelCfg.style = "width: " + this.labelWidth + 'px';
15557 if(this.width * 1 > 0){
15558 contentCfg.style = "width: " + this.width + 'px';
15560 if(this.labelWidth < 13 && this.labelmd == 0){
15561 this.labelmd = this.labelWidth;
15564 if(this.labellg > 0){
15565 labelCfg.cls += ' col-lg-' + this.labellg;
15566 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15569 if(this.labelmd > 0){
15570 labelCfg.cls += ' col-md-' + this.labelmd;
15571 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15574 if(this.labelsm > 0){
15575 labelCfg.cls += ' col-sm-' + this.labelsm;
15576 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15579 if(this.labelxs > 0){
15580 labelCfg.cls += ' col-xs-' + this.labelxs;
15581 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15585 } else if ( this.fieldLabel.length) {
15586 // Roo.log(" label");
15591 //cls : 'input-group-addon',
15592 html : this.fieldLabel
15597 if(this.indicatorpos == 'right'){
15601 //cls : 'input-group-addon',
15602 html : this.fieldLabel
15612 // Roo.log(" no label && no align");
15619 ['xs','sm','md','lg'].map(function(size){
15620 if (settings[size]) {
15621 cfg.cls += ' col-' + size + '-' + settings[size];
15629 _initEventsCalled : false,
15632 initEvents: function()
15634 if (this._initEventsCalled) { // as we call render... prevent looping...
15637 this._initEventsCalled = true;
15640 throw "can not find store for combo";
15643 this.indicator = this.indicatorEl();
15645 this.store = Roo.factory(this.store, Roo.data);
15646 this.store.parent = this;
15648 // if we are building from html. then this element is so complex, that we can not really
15649 // use the rendered HTML.
15650 // so we have to trash and replace the previous code.
15651 if (Roo.XComponent.build_from_html) {
15652 // remove this element....
15653 var e = this.el.dom, k=0;
15654 while (e ) { e = e.previousSibling; ++k;}
15659 this.rendered = false;
15661 this.render(this.parent().getChildContainer(true), k);
15664 if(Roo.isIOS && this.useNativeIOS){
15665 this.initIOSView();
15673 if(Roo.isTouch && this.mobileTouchView){
15674 this.initTouchView();
15679 this.initTickableEvents();
15683 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15685 if(this.hiddenName){
15687 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15689 this.hiddenField.dom.value =
15690 this.hiddenValue !== undefined ? this.hiddenValue :
15691 this.value !== undefined ? this.value : '';
15693 // prevent input submission
15694 this.el.dom.removeAttribute('name');
15695 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15700 // this.el.dom.setAttribute('autocomplete', 'off');
15703 var cls = 'x-combo-list';
15705 //this.list = new Roo.Layer({
15706 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15712 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15713 _this.list.setWidth(lw);
15716 this.list.on('mouseover', this.onViewOver, this);
15717 this.list.on('mousemove', this.onViewMove, this);
15718 this.list.on('scroll', this.onViewScroll, this);
15721 this.list.swallowEvent('mousewheel');
15722 this.assetHeight = 0;
15725 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15726 this.assetHeight += this.header.getHeight();
15729 this.innerList = this.list.createChild({cls:cls+'-inner'});
15730 this.innerList.on('mouseover', this.onViewOver, this);
15731 this.innerList.on('mousemove', this.onViewMove, this);
15732 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15734 if(this.allowBlank && !this.pageSize && !this.disableClear){
15735 this.footer = this.list.createChild({cls:cls+'-ft'});
15736 this.pageTb = new Roo.Toolbar(this.footer);
15740 this.footer = this.list.createChild({cls:cls+'-ft'});
15741 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15742 {pageSize: this.pageSize});
15746 if (this.pageTb && this.allowBlank && !this.disableClear) {
15748 this.pageTb.add(new Roo.Toolbar.Fill(), {
15749 cls: 'x-btn-icon x-btn-clear',
15751 handler: function()
15754 _this.clearValue();
15755 _this.onSelect(false, -1);
15760 this.assetHeight += this.footer.getHeight();
15765 this.tpl = Roo.bootstrap.version == 4 ?
15766 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15767 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15770 this.view = new Roo.View(this.list, this.tpl, {
15771 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15773 //this.view.wrapEl.setDisplayed(false);
15774 this.view.on('click', this.onViewClick, this);
15777 this.store.on('beforeload', this.onBeforeLoad, this);
15778 this.store.on('load', this.onLoad, this);
15779 this.store.on('loadexception', this.onLoadException, this);
15781 if(this.resizable){
15782 this.resizer = new Roo.Resizable(this.list, {
15783 pinned:true, handles:'se'
15785 this.resizer.on('resize', function(r, w, h){
15786 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15787 this.listWidth = w;
15788 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15789 this.restrictHeight();
15791 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15794 if(!this.editable){
15795 this.editable = true;
15796 this.setEditable(false);
15801 if (typeof(this.events.add.listeners) != 'undefined') {
15803 this.addicon = this.wrap.createChild(
15804 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15806 this.addicon.on('click', function(e) {
15807 this.fireEvent('add', this);
15810 if (typeof(this.events.edit.listeners) != 'undefined') {
15812 this.editicon = this.wrap.createChild(
15813 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15814 if (this.addicon) {
15815 this.editicon.setStyle('margin-left', '40px');
15817 this.editicon.on('click', function(e) {
15819 // we fire even if inothing is selected..
15820 this.fireEvent('edit', this, this.lastData );
15826 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15827 "up" : function(e){
15828 this.inKeyMode = true;
15832 "down" : function(e){
15833 if(!this.isExpanded()){
15834 this.onTriggerClick();
15836 this.inKeyMode = true;
15841 "enter" : function(e){
15842 // this.onViewClick();
15846 if(this.fireEvent("specialkey", this, e)){
15847 this.onViewClick(false);
15853 "esc" : function(e){
15857 "tab" : function(e){
15860 if(this.fireEvent("specialkey", this, e)){
15861 this.onViewClick(false);
15869 doRelay : function(foo, bar, hname){
15870 if(hname == 'down' || this.scope.isExpanded()){
15871 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15880 this.queryDelay = Math.max(this.queryDelay || 10,
15881 this.mode == 'local' ? 10 : 250);
15884 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15886 if(this.typeAhead){
15887 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15889 if(this.editable !== false){
15890 this.inputEl().on("keyup", this.onKeyUp, this);
15892 if(this.forceSelection){
15893 this.inputEl().on('blur', this.doForce, this);
15897 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15898 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15902 initTickableEvents: function()
15906 if(this.hiddenName){
15908 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15910 this.hiddenField.dom.value =
15911 this.hiddenValue !== undefined ? this.hiddenValue :
15912 this.value !== undefined ? this.value : '';
15914 // prevent input submission
15915 this.el.dom.removeAttribute('name');
15916 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15921 // this.list = this.el.select('ul.dropdown-menu',true).first();
15923 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15924 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15925 if(this.triggerList){
15926 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15929 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15930 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15932 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15933 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15935 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15936 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15938 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15939 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15940 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15943 this.cancelBtn.hide();
15948 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15949 _this.list.setWidth(lw);
15952 this.list.on('mouseover', this.onViewOver, this);
15953 this.list.on('mousemove', this.onViewMove, this);
15955 this.list.on('scroll', this.onViewScroll, this);
15958 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15959 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15962 this.view = new Roo.View(this.list, this.tpl, {
15967 selectedClass: this.selectedClass
15970 //this.view.wrapEl.setDisplayed(false);
15971 this.view.on('click', this.onViewClick, this);
15975 this.store.on('beforeload', this.onBeforeLoad, this);
15976 this.store.on('load', this.onLoad, this);
15977 this.store.on('loadexception', this.onLoadException, this);
15980 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15981 "up" : function(e){
15982 this.inKeyMode = true;
15986 "down" : function(e){
15987 this.inKeyMode = true;
15991 "enter" : function(e){
15992 if(this.fireEvent("specialkey", this, e)){
15993 this.onViewClick(false);
15999 "esc" : function(e){
16000 this.onTickableFooterButtonClick(e, false, false);
16003 "tab" : function(e){
16004 this.fireEvent("specialkey", this, e);
16006 this.onTickableFooterButtonClick(e, false, false);
16013 doRelay : function(e, fn, key){
16014 if(this.scope.isExpanded()){
16015 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16024 this.queryDelay = Math.max(this.queryDelay || 10,
16025 this.mode == 'local' ? 10 : 250);
16028 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16030 if(this.typeAhead){
16031 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16034 if(this.editable !== false){
16035 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16038 this.indicator = this.indicatorEl();
16040 if(this.indicator){
16041 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16042 this.indicator.hide();
16047 onDestroy : function(){
16049 this.view.setStore(null);
16050 this.view.el.removeAllListeners();
16051 this.view.el.remove();
16052 this.view.purgeListeners();
16055 this.list.dom.innerHTML = '';
16059 this.store.un('beforeload', this.onBeforeLoad, this);
16060 this.store.un('load', this.onLoad, this);
16061 this.store.un('loadexception', this.onLoadException, this);
16063 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16067 fireKey : function(e){
16068 if(e.isNavKeyPress() && !this.list.isVisible()){
16069 this.fireEvent("specialkey", this, e);
16074 onResize: function(w, h)
16078 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16080 // if(typeof w != 'number'){
16081 // // we do not handle it!?!?
16084 // var tw = this.trigger.getWidth();
16085 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16086 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16088 // this.inputEl().setWidth( this.adjustWidth('input', x));
16090 // //this.trigger.setStyle('left', x+'px');
16092 // if(this.list && this.listWidth === undefined){
16093 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16094 // this.list.setWidth(lw);
16095 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16103 * Allow or prevent the user from directly editing the field text. If false is passed,
16104 * the user will only be able to select from the items defined in the dropdown list. This method
16105 * is the runtime equivalent of setting the 'editable' config option at config time.
16106 * @param {Boolean} value True to allow the user to directly edit the field text
16108 setEditable : function(value){
16109 if(value == this.editable){
16112 this.editable = value;
16114 this.inputEl().dom.setAttribute('readOnly', true);
16115 this.inputEl().on('mousedown', this.onTriggerClick, this);
16116 this.inputEl().addClass('x-combo-noedit');
16118 this.inputEl().dom.setAttribute('readOnly', false);
16119 this.inputEl().un('mousedown', this.onTriggerClick, this);
16120 this.inputEl().removeClass('x-combo-noedit');
16126 onBeforeLoad : function(combo,opts){
16127 if(!this.hasFocus){
16131 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16133 this.restrictHeight();
16134 this.selectedIndex = -1;
16138 onLoad : function(){
16140 this.hasQuery = false;
16142 if(!this.hasFocus){
16146 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16147 this.loading.hide();
16150 if(this.store.getCount() > 0){
16153 this.restrictHeight();
16154 if(this.lastQuery == this.allQuery){
16155 if(this.editable && !this.tickable){
16156 this.inputEl().dom.select();
16160 !this.selectByValue(this.value, true) &&
16163 !this.store.lastOptions ||
16164 typeof(this.store.lastOptions.add) == 'undefined' ||
16165 this.store.lastOptions.add != true
16168 this.select(0, true);
16171 if(this.autoFocus){
16174 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16175 this.taTask.delay(this.typeAheadDelay);
16179 this.onEmptyResults();
16185 onLoadException : function()
16187 this.hasQuery = false;
16189 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16190 this.loading.hide();
16193 if(this.tickable && this.editable){
16198 // only causes errors at present
16199 //Roo.log(this.store.reader.jsonData);
16200 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16202 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16208 onTypeAhead : function(){
16209 if(this.store.getCount() > 0){
16210 var r = this.store.getAt(0);
16211 var newValue = r.data[this.displayField];
16212 var len = newValue.length;
16213 var selStart = this.getRawValue().length;
16215 if(selStart != len){
16216 this.setRawValue(newValue);
16217 this.selectText(selStart, newValue.length);
16223 onSelect : function(record, index){
16225 if(this.fireEvent('beforeselect', this, record, index) !== false){
16227 this.setFromData(index > -1 ? record.data : false);
16230 this.fireEvent('select', this, record, index);
16235 * Returns the currently selected field value or empty string if no value is set.
16236 * @return {String} value The selected value
16238 getValue : function()
16240 if(Roo.isIOS && this.useNativeIOS){
16241 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16245 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16248 if(this.valueField){
16249 return typeof this.value != 'undefined' ? this.value : '';
16251 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16255 getRawValue : function()
16257 if(Roo.isIOS && this.useNativeIOS){
16258 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16261 var v = this.inputEl().getValue();
16267 * Clears any text/value currently set in the field
16269 clearValue : function(){
16271 if(this.hiddenField){
16272 this.hiddenField.dom.value = '';
16275 this.setRawValue('');
16276 this.lastSelectionText = '';
16277 this.lastData = false;
16279 var close = this.closeTriggerEl();
16290 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16291 * will be displayed in the field. If the value does not match the data value of an existing item,
16292 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16293 * Otherwise the field will be blank (although the value will still be set).
16294 * @param {String} value The value to match
16296 setValue : function(v)
16298 if(Roo.isIOS && this.useNativeIOS){
16299 this.setIOSValue(v);
16309 if(this.valueField){
16310 var r = this.findRecord(this.valueField, v);
16312 text = r.data[this.displayField];
16313 }else if(this.valueNotFoundText !== undefined){
16314 text = this.valueNotFoundText;
16317 this.lastSelectionText = text;
16318 if(this.hiddenField){
16319 this.hiddenField.dom.value = v;
16321 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16324 var close = this.closeTriggerEl();
16327 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16333 * @property {Object} the last set data for the element
16338 * Sets the value of the field based on a object which is related to the record format for the store.
16339 * @param {Object} value the value to set as. or false on reset?
16341 setFromData : function(o){
16348 var dv = ''; // display value
16349 var vv = ''; // value value..
16351 if (this.displayField) {
16352 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16354 // this is an error condition!!!
16355 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16358 if(this.valueField){
16359 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16362 var close = this.closeTriggerEl();
16365 if(dv.length || vv * 1 > 0){
16367 this.blockFocus=true;
16373 if(this.hiddenField){
16374 this.hiddenField.dom.value = vv;
16376 this.lastSelectionText = dv;
16377 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16381 // no hidden field.. - we store the value in 'value', but still display
16382 // display field!!!!
16383 this.lastSelectionText = dv;
16384 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16391 reset : function(){
16392 // overridden so that last data is reset..
16399 this.setValue(this.originalValue);
16400 //this.clearInvalid();
16401 this.lastData = false;
16403 this.view.clearSelections();
16409 findRecord : function(prop, value){
16411 if(this.store.getCount() > 0){
16412 this.store.each(function(r){
16413 if(r.data[prop] == value){
16423 getName: function()
16425 // returns hidden if it's set..
16426 if (!this.rendered) {return ''};
16427 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16431 onViewMove : function(e, t){
16432 this.inKeyMode = false;
16436 onViewOver : function(e, t){
16437 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16440 var item = this.view.findItemFromChild(t);
16443 var index = this.view.indexOf(item);
16444 this.select(index, false);
16449 onViewClick : function(view, doFocus, el, e)
16451 var index = this.view.getSelectedIndexes()[0];
16453 var r = this.store.getAt(index);
16457 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16464 Roo.each(this.tickItems, function(v,k){
16466 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16468 _this.tickItems.splice(k, 1);
16470 if(typeof(e) == 'undefined' && view == false){
16471 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16483 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16484 this.tickItems.push(r.data);
16487 if(typeof(e) == 'undefined' && view == false){
16488 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16495 this.onSelect(r, index);
16497 if(doFocus !== false && !this.blockFocus){
16498 this.inputEl().focus();
16503 restrictHeight : function(){
16504 //this.innerList.dom.style.height = '';
16505 //var inner = this.innerList.dom;
16506 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16507 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16508 //this.list.beginUpdate();
16509 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16510 this.list.alignTo(this.inputEl(), this.listAlign);
16511 this.list.alignTo(this.inputEl(), this.listAlign);
16512 //this.list.endUpdate();
16516 onEmptyResults : function(){
16518 if(this.tickable && this.editable){
16519 this.hasFocus = false;
16520 this.restrictHeight();
16528 * Returns true if the dropdown list is expanded, else false.
16530 isExpanded : function(){
16531 return this.list.isVisible();
16535 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16536 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16537 * @param {String} value The data value of the item to select
16538 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16539 * selected item if it is not currently in view (defaults to true)
16540 * @return {Boolean} True if the value matched an item in the list, else false
16542 selectByValue : function(v, scrollIntoView){
16543 if(v !== undefined && v !== null){
16544 var r = this.findRecord(this.valueField || this.displayField, v);
16546 this.select(this.store.indexOf(r), scrollIntoView);
16554 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16555 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16556 * @param {Number} index The zero-based index of the list item to select
16557 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16558 * selected item if it is not currently in view (defaults to true)
16560 select : function(index, scrollIntoView){
16561 this.selectedIndex = index;
16562 this.view.select(index);
16563 if(scrollIntoView !== false){
16564 var el = this.view.getNode(index);
16566 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16569 this.list.scrollChildIntoView(el, false);
16575 selectNext : function(){
16576 var ct = this.store.getCount();
16578 if(this.selectedIndex == -1){
16580 }else if(this.selectedIndex < ct-1){
16581 this.select(this.selectedIndex+1);
16587 selectPrev : function(){
16588 var ct = this.store.getCount();
16590 if(this.selectedIndex == -1){
16592 }else if(this.selectedIndex != 0){
16593 this.select(this.selectedIndex-1);
16599 onKeyUp : function(e){
16600 if(this.editable !== false && !e.isSpecialKey()){
16601 this.lastKey = e.getKey();
16602 this.dqTask.delay(this.queryDelay);
16607 validateBlur : function(){
16608 return !this.list || !this.list.isVisible();
16612 initQuery : function(){
16614 var v = this.getRawValue();
16616 if(this.tickable && this.editable){
16617 v = this.tickableInputEl().getValue();
16624 doForce : function(){
16625 if(this.inputEl().dom.value.length > 0){
16626 this.inputEl().dom.value =
16627 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16633 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16634 * query allowing the query action to be canceled if needed.
16635 * @param {String} query The SQL query to execute
16636 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16637 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16638 * saved in the current store (defaults to false)
16640 doQuery : function(q, forceAll){
16642 if(q === undefined || q === null){
16647 forceAll: forceAll,
16651 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16656 forceAll = qe.forceAll;
16657 if(forceAll === true || (q.length >= this.minChars)){
16659 this.hasQuery = true;
16661 if(this.lastQuery != q || this.alwaysQuery){
16662 this.lastQuery = q;
16663 if(this.mode == 'local'){
16664 this.selectedIndex = -1;
16666 this.store.clearFilter();
16669 if(this.specialFilter){
16670 this.fireEvent('specialfilter', this);
16675 this.store.filter(this.displayField, q);
16678 this.store.fireEvent("datachanged", this.store);
16685 this.store.baseParams[this.queryParam] = q;
16687 var options = {params : this.getParams(q)};
16690 options.add = true;
16691 options.params.start = this.page * this.pageSize;
16694 this.store.load(options);
16697 * this code will make the page width larger, at the beginning, the list not align correctly,
16698 * we should expand the list on onLoad
16699 * so command out it
16704 this.selectedIndex = -1;
16709 this.loadNext = false;
16713 getParams : function(q){
16715 //p[this.queryParam] = q;
16719 p.limit = this.pageSize;
16725 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16727 collapse : function(){
16728 if(!this.isExpanded()){
16734 this.hasFocus = false;
16738 this.cancelBtn.hide();
16739 this.trigger.show();
16742 this.tickableInputEl().dom.value = '';
16743 this.tickableInputEl().blur();
16748 Roo.get(document).un('mousedown', this.collapseIf, this);
16749 Roo.get(document).un('mousewheel', this.collapseIf, this);
16750 if (!this.editable) {
16751 Roo.get(document).un('keydown', this.listKeyPress, this);
16753 this.fireEvent('collapse', this);
16759 collapseIf : function(e){
16760 var in_combo = e.within(this.el);
16761 var in_list = e.within(this.list);
16762 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16764 if (in_combo || in_list || is_list) {
16765 //e.stopPropagation();
16770 this.onTickableFooterButtonClick(e, false, false);
16778 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16780 expand : function(){
16782 if(this.isExpanded() || !this.hasFocus){
16786 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16787 this.list.setWidth(lw);
16793 this.restrictHeight();
16797 this.tickItems = Roo.apply([], this.item);
16800 this.cancelBtn.show();
16801 this.trigger.hide();
16804 this.tickableInputEl().focus();
16809 Roo.get(document).on('mousedown', this.collapseIf, this);
16810 Roo.get(document).on('mousewheel', this.collapseIf, this);
16811 if (!this.editable) {
16812 Roo.get(document).on('keydown', this.listKeyPress, this);
16815 this.fireEvent('expand', this);
16819 // Implements the default empty TriggerField.onTriggerClick function
16820 onTriggerClick : function(e)
16822 Roo.log('trigger click');
16824 if(this.disabled || !this.triggerList){
16829 this.loadNext = false;
16831 if(this.isExpanded()){
16833 if (!this.blockFocus) {
16834 this.inputEl().focus();
16838 this.hasFocus = true;
16839 if(this.triggerAction == 'all') {
16840 this.doQuery(this.allQuery, true);
16842 this.doQuery(this.getRawValue());
16844 if (!this.blockFocus) {
16845 this.inputEl().focus();
16850 onTickableTriggerClick : function(e)
16857 this.loadNext = false;
16858 this.hasFocus = true;
16860 if(this.triggerAction == 'all') {
16861 this.doQuery(this.allQuery, true);
16863 this.doQuery(this.getRawValue());
16867 onSearchFieldClick : function(e)
16869 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16870 this.onTickableFooterButtonClick(e, false, false);
16874 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16879 this.loadNext = false;
16880 this.hasFocus = true;
16882 if(this.triggerAction == 'all') {
16883 this.doQuery(this.allQuery, true);
16885 this.doQuery(this.getRawValue());
16889 listKeyPress : function(e)
16891 //Roo.log('listkeypress');
16892 // scroll to first matching element based on key pres..
16893 if (e.isSpecialKey()) {
16896 var k = String.fromCharCode(e.getKey()).toUpperCase();
16899 var csel = this.view.getSelectedNodes();
16900 var cselitem = false;
16902 var ix = this.view.indexOf(csel[0]);
16903 cselitem = this.store.getAt(ix);
16904 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16910 this.store.each(function(v) {
16912 // start at existing selection.
16913 if (cselitem.id == v.id) {
16919 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16920 match = this.store.indexOf(v);
16926 if (match === false) {
16927 return true; // no more action?
16930 this.view.select(match);
16931 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16932 sn.scrollIntoView(sn.dom.parentNode, false);
16935 onViewScroll : function(e, t){
16937 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){
16941 this.hasQuery = true;
16943 this.loading = this.list.select('.loading', true).first();
16945 if(this.loading === null){
16946 this.list.createChild({
16948 cls: 'loading roo-select2-more-results roo-select2-active',
16949 html: 'Loading more results...'
16952 this.loading = this.list.select('.loading', true).first();
16954 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16956 this.loading.hide();
16959 this.loading.show();
16964 this.loadNext = true;
16966 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16971 addItem : function(o)
16973 var dv = ''; // display value
16975 if (this.displayField) {
16976 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16978 // this is an error condition!!!
16979 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16986 var choice = this.choices.createChild({
16988 cls: 'roo-select2-search-choice',
16997 cls: 'roo-select2-search-choice-close fa fa-times',
17002 }, this.searchField);
17004 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17006 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17014 this.inputEl().dom.value = '';
17019 onRemoveItem : function(e, _self, o)
17021 e.preventDefault();
17023 this.lastItem = Roo.apply([], this.item);
17025 var index = this.item.indexOf(o.data) * 1;
17028 Roo.log('not this item?!');
17032 this.item.splice(index, 1);
17037 this.fireEvent('remove', this, e);
17043 syncValue : function()
17045 if(!this.item.length){
17052 Roo.each(this.item, function(i){
17053 if(_this.valueField){
17054 value.push(i[_this.valueField]);
17061 this.value = value.join(',');
17063 if(this.hiddenField){
17064 this.hiddenField.dom.value = this.value;
17067 this.store.fireEvent("datachanged", this.store);
17072 clearItem : function()
17074 if(!this.multiple){
17080 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17088 if(this.tickable && !Roo.isTouch){
17089 this.view.refresh();
17093 inputEl: function ()
17095 if(Roo.isIOS && this.useNativeIOS){
17096 return this.el.select('select.roo-ios-select', true).first();
17099 if(Roo.isTouch && this.mobileTouchView){
17100 return this.el.select('input.form-control',true).first();
17104 return this.searchField;
17107 return this.el.select('input.form-control',true).first();
17110 onTickableFooterButtonClick : function(e, btn, el)
17112 e.preventDefault();
17114 this.lastItem = Roo.apply([], this.item);
17116 if(btn && btn.name == 'cancel'){
17117 this.tickItems = Roo.apply([], this.item);
17126 Roo.each(this.tickItems, function(o){
17134 validate : function()
17136 if(this.getVisibilityEl().hasClass('hidden')){
17140 var v = this.getRawValue();
17143 v = this.getValue();
17146 if(this.disabled || this.allowBlank || v.length){
17151 this.markInvalid();
17155 tickableInputEl : function()
17157 if(!this.tickable || !this.editable){
17158 return this.inputEl();
17161 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17165 getAutoCreateTouchView : function()
17170 cls: 'form-group' //input-group
17176 type : this.inputType,
17177 cls : 'form-control x-combo-noedit',
17178 autocomplete: 'new-password',
17179 placeholder : this.placeholder || '',
17184 input.name = this.name;
17188 input.cls += ' input-' + this.size;
17191 if (this.disabled) {
17192 input.disabled = true;
17196 cls : 'roo-combobox-wrap',
17203 inputblock.cls += ' input-group';
17205 inputblock.cn.unshift({
17207 cls : 'input-group-addon input-group-prepend input-group-text',
17212 if(this.removable && !this.multiple){
17213 inputblock.cls += ' roo-removable';
17215 inputblock.cn.push({
17218 cls : 'roo-combo-removable-btn close'
17222 if(this.hasFeedback && !this.allowBlank){
17224 inputblock.cls += ' has-feedback';
17226 inputblock.cn.push({
17228 cls: 'glyphicon form-control-feedback'
17235 inputblock.cls += (this.before) ? '' : ' input-group';
17237 inputblock.cn.push({
17239 cls : 'input-group-addon input-group-append input-group-text',
17245 var ibwrap = inputblock;
17250 cls: 'roo-select2-choices',
17254 cls: 'roo-select2-search-field',
17267 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17272 cls: 'form-hidden-field'
17278 if(!this.multiple && this.showToggleBtn){
17284 if (this.caret != false) {
17287 cls: 'fa fa-' + this.caret
17294 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17296 Roo.bootstrap.version == 3 ? caret : '',
17299 cls: 'combobox-clear',
17313 combobox.cls += ' roo-select2-container-multi';
17316 var align = this.labelAlign || this.parentLabelAlign();
17318 if (align ==='left' && this.fieldLabel.length) {
17323 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17324 tooltip : 'This field is required'
17328 cls : 'control-label col-form-label',
17329 html : this.fieldLabel
17333 cls : 'roo-combobox-wrap ',
17340 var labelCfg = cfg.cn[1];
17341 var contentCfg = cfg.cn[2];
17344 if(this.indicatorpos == 'right'){
17349 cls : 'control-label col-form-label',
17353 html : this.fieldLabel
17357 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17358 tooltip : 'This field is required'
17363 cls : "roo-combobox-wrap ",
17371 labelCfg = cfg.cn[0];
17372 contentCfg = cfg.cn[1];
17377 if(this.labelWidth > 12){
17378 labelCfg.style = "width: " + this.labelWidth + 'px';
17381 if(this.labelWidth < 13 && this.labelmd == 0){
17382 this.labelmd = this.labelWidth;
17385 if(this.labellg > 0){
17386 labelCfg.cls += ' col-lg-' + this.labellg;
17387 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17390 if(this.labelmd > 0){
17391 labelCfg.cls += ' col-md-' + this.labelmd;
17392 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17395 if(this.labelsm > 0){
17396 labelCfg.cls += ' col-sm-' + this.labelsm;
17397 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17400 if(this.labelxs > 0){
17401 labelCfg.cls += ' col-xs-' + this.labelxs;
17402 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17406 } else if ( this.fieldLabel.length) {
17410 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17411 tooltip : 'This field is required'
17415 cls : 'control-label',
17416 html : this.fieldLabel
17427 if(this.indicatorpos == 'right'){
17431 cls : 'control-label',
17432 html : this.fieldLabel,
17436 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17437 tooltip : 'This field is required'
17454 var settings = this;
17456 ['xs','sm','md','lg'].map(function(size){
17457 if (settings[size]) {
17458 cfg.cls += ' col-' + size + '-' + settings[size];
17465 initTouchView : function()
17467 this.renderTouchView();
17469 this.touchViewEl.on('scroll', function(){
17470 this.el.dom.scrollTop = 0;
17473 this.originalValue = this.getValue();
17475 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17477 this.inputEl().on("click", this.showTouchView, this);
17478 if (this.triggerEl) {
17479 this.triggerEl.on("click", this.showTouchView, this);
17483 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17484 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17486 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17488 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17489 this.store.on('load', this.onTouchViewLoad, this);
17490 this.store.on('loadexception', this.onTouchViewLoadException, this);
17492 if(this.hiddenName){
17494 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17496 this.hiddenField.dom.value =
17497 this.hiddenValue !== undefined ? this.hiddenValue :
17498 this.value !== undefined ? this.value : '';
17500 this.el.dom.removeAttribute('name');
17501 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17505 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17506 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17509 if(this.removable && !this.multiple){
17510 var close = this.closeTriggerEl();
17512 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17513 close.on('click', this.removeBtnClick, this, close);
17517 * fix the bug in Safari iOS8
17519 this.inputEl().on("focus", function(e){
17520 document.activeElement.blur();
17523 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17530 renderTouchView : function()
17532 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17533 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17535 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17536 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17538 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17539 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17540 this.touchViewBodyEl.setStyle('overflow', 'auto');
17542 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17543 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17545 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17546 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17550 showTouchView : function()
17556 this.touchViewHeaderEl.hide();
17558 if(this.modalTitle.length){
17559 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17560 this.touchViewHeaderEl.show();
17563 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17564 this.touchViewEl.show();
17566 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17568 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17569 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17571 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17573 if(this.modalTitle.length){
17574 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17577 this.touchViewBodyEl.setHeight(bodyHeight);
17581 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17583 this.touchViewEl.addClass(['in','show']);
17586 if(this._touchViewMask){
17587 Roo.get(document.body).addClass("x-body-masked");
17588 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17589 this._touchViewMask.setStyle('z-index', 10000);
17590 this._touchViewMask.addClass('show');
17593 this.doTouchViewQuery();
17597 hideTouchView : function()
17599 this.touchViewEl.removeClass(['in','show']);
17603 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17605 this.touchViewEl.setStyle('display', 'none');
17608 if(this._touchViewMask){
17609 this._touchViewMask.removeClass('show');
17610 Roo.get(document.body).removeClass("x-body-masked");
17614 setTouchViewValue : function()
17621 Roo.each(this.tickItems, function(o){
17626 this.hideTouchView();
17629 doTouchViewQuery : function()
17638 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17642 if(!this.alwaysQuery || this.mode == 'local'){
17643 this.onTouchViewLoad();
17650 onTouchViewBeforeLoad : function(combo,opts)
17656 onTouchViewLoad : function()
17658 if(this.store.getCount() < 1){
17659 this.onTouchViewEmptyResults();
17663 this.clearTouchView();
17665 var rawValue = this.getRawValue();
17667 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17669 this.tickItems = [];
17671 this.store.data.each(function(d, rowIndex){
17672 var row = this.touchViewListGroup.createChild(template);
17674 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17675 row.addClass(d.data.cls);
17678 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17681 html : d.data[this.displayField]
17684 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17685 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17688 row.removeClass('selected');
17689 if(!this.multiple && this.valueField &&
17690 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17693 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694 row.addClass('selected');
17697 if(this.multiple && this.valueField &&
17698 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17702 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17703 this.tickItems.push(d.data);
17706 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17710 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17712 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17714 if(this.modalTitle.length){
17715 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17718 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17720 if(this.mobile_restrict_height && listHeight < bodyHeight){
17721 this.touchViewBodyEl.setHeight(listHeight);
17726 if(firstChecked && listHeight > bodyHeight){
17727 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17732 onTouchViewLoadException : function()
17734 this.hideTouchView();
17737 onTouchViewEmptyResults : function()
17739 this.clearTouchView();
17741 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17743 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17747 clearTouchView : function()
17749 this.touchViewListGroup.dom.innerHTML = '';
17752 onTouchViewClick : function(e, el, o)
17754 e.preventDefault();
17757 var rowIndex = o.rowIndex;
17759 var r = this.store.getAt(rowIndex);
17761 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17763 if(!this.multiple){
17764 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17765 c.dom.removeAttribute('checked');
17768 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17770 this.setFromData(r.data);
17772 var close = this.closeTriggerEl();
17778 this.hideTouchView();
17780 this.fireEvent('select', this, r, rowIndex);
17785 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17786 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17787 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17791 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17792 this.addItem(r.data);
17793 this.tickItems.push(r.data);
17797 getAutoCreateNativeIOS : function()
17800 cls: 'form-group' //input-group,
17805 cls : 'roo-ios-select'
17809 combobox.name = this.name;
17812 if (this.disabled) {
17813 combobox.disabled = true;
17816 var settings = this;
17818 ['xs','sm','md','lg'].map(function(size){
17819 if (settings[size]) {
17820 cfg.cls += ' col-' + size + '-' + settings[size];
17830 initIOSView : function()
17832 this.store.on('load', this.onIOSViewLoad, this);
17837 onIOSViewLoad : function()
17839 if(this.store.getCount() < 1){
17843 this.clearIOSView();
17845 if(this.allowBlank) {
17847 var default_text = '-- SELECT --';
17849 if(this.placeholder.length){
17850 default_text = this.placeholder;
17853 if(this.emptyTitle.length){
17854 default_text += ' - ' + this.emptyTitle + ' -';
17857 var opt = this.inputEl().createChild({
17860 html : default_text
17864 o[this.valueField] = 0;
17865 o[this.displayField] = default_text;
17867 this.ios_options.push({
17874 this.store.data.each(function(d, rowIndex){
17878 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17879 html = d.data[this.displayField];
17884 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17885 value = d.data[this.valueField];
17894 if(this.value == d.data[this.valueField]){
17895 option['selected'] = true;
17898 var opt = this.inputEl().createChild(option);
17900 this.ios_options.push({
17907 this.inputEl().on('change', function(){
17908 this.fireEvent('select', this);
17913 clearIOSView: function()
17915 this.inputEl().dom.innerHTML = '';
17917 this.ios_options = [];
17920 setIOSValue: function(v)
17924 if(!this.ios_options){
17928 Roo.each(this.ios_options, function(opts){
17930 opts.el.dom.removeAttribute('selected');
17932 if(opts.data[this.valueField] != v){
17936 opts.el.dom.setAttribute('selected', true);
17942 * @cfg {Boolean} grow
17946 * @cfg {Number} growMin
17950 * @cfg {Number} growMax
17959 Roo.apply(Roo.bootstrap.ComboBox, {
17963 cls: 'modal-header',
17985 cls: 'list-group-item',
17989 cls: 'roo-combobox-list-group-item-value'
17993 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18007 listItemCheckbox : {
18009 cls: 'list-group-item',
18013 cls: 'roo-combobox-list-group-item-value'
18017 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18033 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18038 cls: 'modal-footer',
18046 cls: 'col-xs-6 text-left',
18049 cls: 'btn btn-danger roo-touch-view-cancel',
18055 cls: 'col-xs-6 text-right',
18058 cls: 'btn btn-success roo-touch-view-ok',
18069 Roo.apply(Roo.bootstrap.ComboBox, {
18071 touchViewTemplate : {
18073 cls: 'modal fade roo-combobox-touch-view',
18077 cls: 'modal-dialog',
18078 style : 'position:fixed', // we have to fix position....
18082 cls: 'modal-content',
18084 Roo.bootstrap.ComboBox.header,
18085 Roo.bootstrap.ComboBox.body,
18086 Roo.bootstrap.ComboBox.footer
18095 * Ext JS Library 1.1.1
18096 * Copyright(c) 2006-2007, Ext JS, LLC.
18098 * Originally Released Under LGPL - original licence link has changed is not relivant.
18101 * <script type="text/javascript">
18106 * @extends Roo.util.Observable
18107 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18108 * This class also supports single and multi selection modes. <br>
18109 * Create a data model bound view:
18111 var store = new Roo.data.Store(...);
18113 var view = new Roo.View({
18115 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18117 singleSelect: true,
18118 selectedClass: "ydataview-selected",
18122 // listen for node click?
18123 view.on("click", function(vw, index, node, e){
18124 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18128 dataModel.load("foobar.xml");
18130 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18132 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18133 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18135 * Note: old style constructor is still suported (container, template, config)
18138 * Create a new View
18139 * @param {Object} config The config object
18142 Roo.View = function(config, depreciated_tpl, depreciated_config){
18144 this.parent = false;
18146 if (typeof(depreciated_tpl) == 'undefined') {
18147 // new way.. - universal constructor.
18148 Roo.apply(this, config);
18149 this.el = Roo.get(this.el);
18152 this.el = Roo.get(config);
18153 this.tpl = depreciated_tpl;
18154 Roo.apply(this, depreciated_config);
18156 this.wrapEl = this.el.wrap().wrap();
18157 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18160 if(typeof(this.tpl) == "string"){
18161 this.tpl = new Roo.Template(this.tpl);
18163 // support xtype ctors..
18164 this.tpl = new Roo.factory(this.tpl, Roo);
18168 this.tpl.compile();
18173 * @event beforeclick
18174 * Fires before a click is processed. Returns false to cancel the default action.
18175 * @param {Roo.View} this
18176 * @param {Number} index The index of the target node
18177 * @param {HTMLElement} node The target node
18178 * @param {Roo.EventObject} e The raw event object
18180 "beforeclick" : true,
18183 * Fires when a template node is clicked.
18184 * @param {Roo.View} this
18185 * @param {Number} index The index of the target node
18186 * @param {HTMLElement} node The target node
18187 * @param {Roo.EventObject} e The raw event object
18192 * Fires when a template node is double clicked.
18193 * @param {Roo.View} this
18194 * @param {Number} index The index of the target node
18195 * @param {HTMLElement} node The target node
18196 * @param {Roo.EventObject} e The raw event object
18200 * @event contextmenu
18201 * Fires when a template node is right clicked.
18202 * @param {Roo.View} this
18203 * @param {Number} index The index of the target node
18204 * @param {HTMLElement} node The target node
18205 * @param {Roo.EventObject} e The raw event object
18207 "contextmenu" : true,
18209 * @event selectionchange
18210 * Fires when the selected nodes change.
18211 * @param {Roo.View} this
18212 * @param {Array} selections Array of the selected nodes
18214 "selectionchange" : true,
18217 * @event beforeselect
18218 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18219 * @param {Roo.View} this
18220 * @param {HTMLElement} node The node to be selected
18221 * @param {Array} selections Array of currently selected nodes
18223 "beforeselect" : true,
18225 * @event preparedata
18226 * Fires on every row to render, to allow you to change the data.
18227 * @param {Roo.View} this
18228 * @param {Object} data to be rendered (change this)
18230 "preparedata" : true
18238 "click": this.onClick,
18239 "dblclick": this.onDblClick,
18240 "contextmenu": this.onContextMenu,
18244 this.selections = [];
18246 this.cmp = new Roo.CompositeElementLite([]);
18248 this.store = Roo.factory(this.store, Roo.data);
18249 this.setStore(this.store, true);
18252 if ( this.footer && this.footer.xtype) {
18254 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18256 this.footer.dataSource = this.store;
18257 this.footer.container = fctr;
18258 this.footer = Roo.factory(this.footer, Roo);
18259 fctr.insertFirst(this.el);
18261 // this is a bit insane - as the paging toolbar seems to detach the el..
18262 // dom.parentNode.parentNode.parentNode
18263 // they get detached?
18267 Roo.View.superclass.constructor.call(this);
18272 Roo.extend(Roo.View, Roo.util.Observable, {
18275 * @cfg {Roo.data.Store} store Data store to load data from.
18280 * @cfg {String|Roo.Element} el The container element.
18285 * @cfg {String|Roo.Template} tpl The template used by this View
18289 * @cfg {String} dataName the named area of the template to use as the data area
18290 * Works with domtemplates roo-name="name"
18294 * @cfg {String} selectedClass The css class to add to selected nodes
18296 selectedClass : "x-view-selected",
18298 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18303 * @cfg {String} text to display on mask (default Loading)
18307 * @cfg {Boolean} multiSelect Allow multiple selection
18309 multiSelect : false,
18311 * @cfg {Boolean} singleSelect Allow single selection
18313 singleSelect: false,
18316 * @cfg {Boolean} toggleSelect - selecting
18318 toggleSelect : false,
18321 * @cfg {Boolean} tickable - selecting
18326 * Returns the element this view is bound to.
18327 * @return {Roo.Element}
18329 getEl : function(){
18330 return this.wrapEl;
18336 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18338 refresh : function(){
18339 //Roo.log('refresh');
18342 // if we are using something like 'domtemplate', then
18343 // the what gets used is:
18344 // t.applySubtemplate(NAME, data, wrapping data..)
18345 // the outer template then get' applied with
18346 // the store 'extra data'
18347 // and the body get's added to the
18348 // roo-name="data" node?
18349 // <span class='roo-tpl-{name}'></span> ?????
18353 this.clearSelections();
18354 this.el.update("");
18356 var records = this.store.getRange();
18357 if(records.length < 1) {
18359 // is this valid?? = should it render a template??
18361 this.el.update(this.emptyText);
18365 if (this.dataName) {
18366 this.el.update(t.apply(this.store.meta)); //????
18367 el = this.el.child('.roo-tpl-' + this.dataName);
18370 for(var i = 0, len = records.length; i < len; i++){
18371 var data = this.prepareData(records[i].data, i, records[i]);
18372 this.fireEvent("preparedata", this, data, i, records[i]);
18374 var d = Roo.apply({}, data);
18377 Roo.apply(d, {'roo-id' : Roo.id()});
18381 Roo.each(this.parent.item, function(item){
18382 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18385 Roo.apply(d, {'roo-data-checked' : 'checked'});
18389 html[html.length] = Roo.util.Format.trim(
18391 t.applySubtemplate(this.dataName, d, this.store.meta) :
18398 el.update(html.join(""));
18399 this.nodes = el.dom.childNodes;
18400 this.updateIndexes(0);
18405 * Function to override to reformat the data that is sent to
18406 * the template for each node.
18407 * DEPRICATED - use the preparedata event handler.
18408 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18409 * a JSON object for an UpdateManager bound view).
18411 prepareData : function(data, index, record)
18413 this.fireEvent("preparedata", this, data, index, record);
18417 onUpdate : function(ds, record){
18418 // Roo.log('on update');
18419 this.clearSelections();
18420 var index = this.store.indexOf(record);
18421 var n = this.nodes[index];
18422 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18423 n.parentNode.removeChild(n);
18424 this.updateIndexes(index, index);
18430 onAdd : function(ds, records, index)
18432 //Roo.log(['on Add', ds, records, index] );
18433 this.clearSelections();
18434 if(this.nodes.length == 0){
18438 var n = this.nodes[index];
18439 for(var i = 0, len = records.length; i < len; i++){
18440 var d = this.prepareData(records[i].data, i, records[i]);
18442 this.tpl.insertBefore(n, d);
18445 this.tpl.append(this.el, d);
18448 this.updateIndexes(index);
18451 onRemove : function(ds, record, index){
18452 // Roo.log('onRemove');
18453 this.clearSelections();
18454 var el = this.dataName ?
18455 this.el.child('.roo-tpl-' + this.dataName) :
18458 el.dom.removeChild(this.nodes[index]);
18459 this.updateIndexes(index);
18463 * Refresh an individual node.
18464 * @param {Number} index
18466 refreshNode : function(index){
18467 this.onUpdate(this.store, this.store.getAt(index));
18470 updateIndexes : function(startIndex, endIndex){
18471 var ns = this.nodes;
18472 startIndex = startIndex || 0;
18473 endIndex = endIndex || ns.length - 1;
18474 for(var i = startIndex; i <= endIndex; i++){
18475 ns[i].nodeIndex = i;
18480 * Changes the data store this view uses and refresh the view.
18481 * @param {Store} store
18483 setStore : function(store, initial){
18484 if(!initial && this.store){
18485 this.store.un("datachanged", this.refresh);
18486 this.store.un("add", this.onAdd);
18487 this.store.un("remove", this.onRemove);
18488 this.store.un("update", this.onUpdate);
18489 this.store.un("clear", this.refresh);
18490 this.store.un("beforeload", this.onBeforeLoad);
18491 this.store.un("load", this.onLoad);
18492 this.store.un("loadexception", this.onLoad);
18496 store.on("datachanged", this.refresh, this);
18497 store.on("add", this.onAdd, this);
18498 store.on("remove", this.onRemove, this);
18499 store.on("update", this.onUpdate, this);
18500 store.on("clear", this.refresh, this);
18501 store.on("beforeload", this.onBeforeLoad, this);
18502 store.on("load", this.onLoad, this);
18503 store.on("loadexception", this.onLoad, this);
18511 * onbeforeLoad - masks the loading area.
18514 onBeforeLoad : function(store,opts)
18516 //Roo.log('onBeforeLoad');
18518 this.el.update("");
18520 this.el.mask(this.mask ? this.mask : "Loading" );
18522 onLoad : function ()
18529 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18530 * @param {HTMLElement} node
18531 * @return {HTMLElement} The template node
18533 findItemFromChild : function(node){
18534 var el = this.dataName ?
18535 this.el.child('.roo-tpl-' + this.dataName,true) :
18538 if(!node || node.parentNode == el){
18541 var p = node.parentNode;
18542 while(p && p != el){
18543 if(p.parentNode == el){
18552 onClick : function(e){
18553 var item = this.findItemFromChild(e.getTarget());
18555 var index = this.indexOf(item);
18556 if(this.onItemClick(item, index, e) !== false){
18557 this.fireEvent("click", this, index, item, e);
18560 this.clearSelections();
18565 onContextMenu : function(e){
18566 var item = this.findItemFromChild(e.getTarget());
18568 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18573 onDblClick : function(e){
18574 var item = this.findItemFromChild(e.getTarget());
18576 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18580 onItemClick : function(item, index, e)
18582 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18585 if (this.toggleSelect) {
18586 var m = this.isSelected(item) ? 'unselect' : 'select';
18589 _t[m](item, true, false);
18592 if(this.multiSelect || this.singleSelect){
18593 if(this.multiSelect && e.shiftKey && this.lastSelection){
18594 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18596 this.select(item, this.multiSelect && e.ctrlKey);
18597 this.lastSelection = item;
18600 if(!this.tickable){
18601 e.preventDefault();
18609 * Get the number of selected nodes.
18612 getSelectionCount : function(){
18613 return this.selections.length;
18617 * Get the currently selected nodes.
18618 * @return {Array} An array of HTMLElements
18620 getSelectedNodes : function(){
18621 return this.selections;
18625 * Get the indexes of the selected nodes.
18628 getSelectedIndexes : function(){
18629 var indexes = [], s = this.selections;
18630 for(var i = 0, len = s.length; i < len; i++){
18631 indexes.push(s[i].nodeIndex);
18637 * Clear all selections
18638 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18640 clearSelections : function(suppressEvent){
18641 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18642 this.cmp.elements = this.selections;
18643 this.cmp.removeClass(this.selectedClass);
18644 this.selections = [];
18645 if(!suppressEvent){
18646 this.fireEvent("selectionchange", this, this.selections);
18652 * Returns true if the passed node is selected
18653 * @param {HTMLElement/Number} node The node or node index
18654 * @return {Boolean}
18656 isSelected : function(node){
18657 var s = this.selections;
18661 node = this.getNode(node);
18662 return s.indexOf(node) !== -1;
18667 * @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
18668 * @param {Boolean} keepExisting (optional) true to keep existing selections
18669 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18671 select : function(nodeInfo, keepExisting, suppressEvent){
18672 if(nodeInfo instanceof Array){
18674 this.clearSelections(true);
18676 for(var i = 0, len = nodeInfo.length; i < len; i++){
18677 this.select(nodeInfo[i], true, true);
18681 var node = this.getNode(nodeInfo);
18682 if(!node || this.isSelected(node)){
18683 return; // already selected.
18686 this.clearSelections(true);
18689 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18690 Roo.fly(node).addClass(this.selectedClass);
18691 this.selections.push(node);
18692 if(!suppressEvent){
18693 this.fireEvent("selectionchange", this, this.selections);
18701 * @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
18702 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18703 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18705 unselect : function(nodeInfo, keepExisting, suppressEvent)
18707 if(nodeInfo instanceof Array){
18708 Roo.each(this.selections, function(s) {
18709 this.unselect(s, nodeInfo);
18713 var node = this.getNode(nodeInfo);
18714 if(!node || !this.isSelected(node)){
18715 //Roo.log("not selected");
18716 return; // not selected.
18720 Roo.each(this.selections, function(s) {
18722 Roo.fly(node).removeClass(this.selectedClass);
18729 this.selections= ns;
18730 this.fireEvent("selectionchange", this, this.selections);
18734 * Gets a template node.
18735 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18736 * @return {HTMLElement} The node or null if it wasn't found
18738 getNode : function(nodeInfo){
18739 if(typeof nodeInfo == "string"){
18740 return document.getElementById(nodeInfo);
18741 }else if(typeof nodeInfo == "number"){
18742 return this.nodes[nodeInfo];
18748 * Gets a range template nodes.
18749 * @param {Number} startIndex
18750 * @param {Number} endIndex
18751 * @return {Array} An array of nodes
18753 getNodes : function(start, end){
18754 var ns = this.nodes;
18755 start = start || 0;
18756 end = typeof end == "undefined" ? ns.length - 1 : end;
18759 for(var i = start; i <= end; i++){
18763 for(var i = start; i >= end; i--){
18771 * Finds the index of the passed node
18772 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18773 * @return {Number} The index of the node or -1
18775 indexOf : function(node){
18776 node = this.getNode(node);
18777 if(typeof node.nodeIndex == "number"){
18778 return node.nodeIndex;
18780 var ns = this.nodes;
18781 for(var i = 0, len = ns.length; i < len; i++){
18792 * based on jquery fullcalendar
18796 Roo.bootstrap = Roo.bootstrap || {};
18798 * @class Roo.bootstrap.Calendar
18799 * @extends Roo.bootstrap.Component
18800 * Bootstrap Calendar class
18801 * @cfg {Boolean} loadMask (true|false) default false
18802 * @cfg {Object} header generate the user specific header of the calendar, default false
18805 * Create a new Container
18806 * @param {Object} config The config object
18811 Roo.bootstrap.Calendar = function(config){
18812 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18816 * Fires when a date is selected
18817 * @param {DatePicker} this
18818 * @param {Date} date The selected date
18822 * @event monthchange
18823 * Fires when the displayed month changes
18824 * @param {DatePicker} this
18825 * @param {Date} date The selected month
18827 'monthchange': true,
18829 * @event evententer
18830 * Fires when mouse over an event
18831 * @param {Calendar} this
18832 * @param {event} Event
18834 'evententer': true,
18836 * @event eventleave
18837 * Fires when the mouse leaves an
18838 * @param {Calendar} this
18841 'eventleave': true,
18843 * @event eventclick
18844 * Fires when the mouse click an
18845 * @param {Calendar} this
18854 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18857 * @cfg {Number} startDay
18858 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18866 getAutoCreate : function(){
18869 var fc_button = function(name, corner, style, content ) {
18870 return Roo.apply({},{
18872 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18874 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18877 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18888 style : 'width:100%',
18895 cls : 'fc-header-left',
18897 fc_button('prev', 'left', 'arrow', '‹' ),
18898 fc_button('next', 'right', 'arrow', '›' ),
18899 { tag: 'span', cls: 'fc-header-space' },
18900 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18908 cls : 'fc-header-center',
18912 cls: 'fc-header-title',
18915 html : 'month / year'
18923 cls : 'fc-header-right',
18925 /* fc_button('month', 'left', '', 'month' ),
18926 fc_button('week', '', '', 'week' ),
18927 fc_button('day', 'right', '', 'day' )
18939 header = this.header;
18942 var cal_heads = function() {
18944 // fixme - handle this.
18946 for (var i =0; i < Date.dayNames.length; i++) {
18947 var d = Date.dayNames[i];
18950 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18951 html : d.substring(0,3)
18955 ret[0].cls += ' fc-first';
18956 ret[6].cls += ' fc-last';
18959 var cal_cell = function(n) {
18962 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18967 cls: 'fc-day-number',
18971 cls: 'fc-day-content',
18975 style: 'position: relative;' // height: 17px;
18987 var cal_rows = function() {
18990 for (var r = 0; r < 6; r++) {
18997 for (var i =0; i < Date.dayNames.length; i++) {
18998 var d = Date.dayNames[i];
18999 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19002 row.cn[0].cls+=' fc-first';
19003 row.cn[0].cn[0].style = 'min-height:90px';
19004 row.cn[6].cls+=' fc-last';
19008 ret[0].cls += ' fc-first';
19009 ret[4].cls += ' fc-prev-last';
19010 ret[5].cls += ' fc-last';
19017 cls: 'fc-border-separate',
19018 style : 'width:100%',
19026 cls : 'fc-first fc-last',
19044 cls : 'fc-content',
19045 style : "position: relative;",
19048 cls : 'fc-view fc-view-month fc-grid',
19049 style : 'position: relative',
19050 unselectable : 'on',
19053 cls : 'fc-event-container',
19054 style : 'position:absolute;z-index:8;top:0;left:0;'
19072 initEvents : function()
19075 throw "can not find store for calendar";
19081 style: "text-align:center",
19085 style: "background-color:white;width:50%;margin:250 auto",
19089 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19100 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19102 var size = this.el.select('.fc-content', true).first().getSize();
19103 this.maskEl.setSize(size.width, size.height);
19104 this.maskEl.enableDisplayMode("block");
19105 if(!this.loadMask){
19106 this.maskEl.hide();
19109 this.store = Roo.factory(this.store, Roo.data);
19110 this.store.on('load', this.onLoad, this);
19111 this.store.on('beforeload', this.onBeforeLoad, this);
19115 this.cells = this.el.select('.fc-day',true);
19116 //Roo.log(this.cells);
19117 this.textNodes = this.el.query('.fc-day-number');
19118 this.cells.addClassOnOver('fc-state-hover');
19120 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19121 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19122 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19123 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19125 this.on('monthchange', this.onMonthChange, this);
19127 this.update(new Date().clearTime());
19130 resize : function() {
19131 var sz = this.el.getSize();
19133 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19134 this.el.select('.fc-day-content div',true).setHeight(34);
19139 showPrevMonth : function(e){
19140 this.update(this.activeDate.add("mo", -1));
19142 showToday : function(e){
19143 this.update(new Date().clearTime());
19146 showNextMonth : function(e){
19147 this.update(this.activeDate.add("mo", 1));
19151 showPrevYear : function(){
19152 this.update(this.activeDate.add("y", -1));
19156 showNextYear : function(){
19157 this.update(this.activeDate.add("y", 1));
19162 update : function(date)
19164 var vd = this.activeDate;
19165 this.activeDate = date;
19166 // if(vd && this.el){
19167 // var t = date.getTime();
19168 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19169 // Roo.log('using add remove');
19171 // this.fireEvent('monthchange', this, date);
19173 // this.cells.removeClass("fc-state-highlight");
19174 // this.cells.each(function(c){
19175 // if(c.dateValue == t){
19176 // c.addClass("fc-state-highlight");
19177 // setTimeout(function(){
19178 // try{c.dom.firstChild.focus();}catch(e){}
19188 var days = date.getDaysInMonth();
19190 var firstOfMonth = date.getFirstDateOfMonth();
19191 var startingPos = firstOfMonth.getDay()-this.startDay;
19193 if(startingPos < this.startDay){
19197 var pm = date.add(Date.MONTH, -1);
19198 var prevStart = pm.getDaysInMonth()-startingPos;
19200 this.cells = this.el.select('.fc-day',true);
19201 this.textNodes = this.el.query('.fc-day-number');
19202 this.cells.addClassOnOver('fc-state-hover');
19204 var cells = this.cells.elements;
19205 var textEls = this.textNodes;
19207 Roo.each(cells, function(cell){
19208 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19211 days += startingPos;
19213 // convert everything to numbers so it's fast
19214 var day = 86400000;
19215 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19218 //Roo.log(prevStart);
19220 var today = new Date().clearTime().getTime();
19221 var sel = date.clearTime().getTime();
19222 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19223 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19224 var ddMatch = this.disabledDatesRE;
19225 var ddText = this.disabledDatesText;
19226 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19227 var ddaysText = this.disabledDaysText;
19228 var format = this.format;
19230 var setCellClass = function(cal, cell){
19234 //Roo.log('set Cell Class');
19236 var t = d.getTime();
19240 cell.dateValue = t;
19242 cell.className += " fc-today";
19243 cell.className += " fc-state-highlight";
19244 cell.title = cal.todayText;
19247 // disable highlight in other month..
19248 //cell.className += " fc-state-highlight";
19253 cell.className = " fc-state-disabled";
19254 cell.title = cal.minText;
19258 cell.className = " fc-state-disabled";
19259 cell.title = cal.maxText;
19263 if(ddays.indexOf(d.getDay()) != -1){
19264 cell.title = ddaysText;
19265 cell.className = " fc-state-disabled";
19268 if(ddMatch && format){
19269 var fvalue = d.dateFormat(format);
19270 if(ddMatch.test(fvalue)){
19271 cell.title = ddText.replace("%0", fvalue);
19272 cell.className = " fc-state-disabled";
19276 if (!cell.initialClassName) {
19277 cell.initialClassName = cell.dom.className;
19280 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19285 for(; i < startingPos; i++) {
19286 textEls[i].innerHTML = (++prevStart);
19287 d.setDate(d.getDate()+1);
19289 cells[i].className = "fc-past fc-other-month";
19290 setCellClass(this, cells[i]);
19295 for(; i < days; i++){
19296 intDay = i - startingPos + 1;
19297 textEls[i].innerHTML = (intDay);
19298 d.setDate(d.getDate()+1);
19300 cells[i].className = ''; // "x-date-active";
19301 setCellClass(this, cells[i]);
19305 for(; i < 42; i++) {
19306 textEls[i].innerHTML = (++extraDays);
19307 d.setDate(d.getDate()+1);
19309 cells[i].className = "fc-future fc-other-month";
19310 setCellClass(this, cells[i]);
19313 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19315 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19317 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19318 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19320 if(totalRows != 6){
19321 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19322 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19325 this.fireEvent('monthchange', this, date);
19329 if(!this.internalRender){
19330 var main = this.el.dom.firstChild;
19331 var w = main.offsetWidth;
19332 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19333 Roo.fly(main).setWidth(w);
19334 this.internalRender = true;
19335 // opera does not respect the auto grow header center column
19336 // then, after it gets a width opera refuses to recalculate
19337 // without a second pass
19338 if(Roo.isOpera && !this.secondPass){
19339 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19340 this.secondPass = true;
19341 this.update.defer(10, this, [date]);
19348 findCell : function(dt) {
19349 dt = dt.clearTime().getTime();
19351 this.cells.each(function(c){
19352 //Roo.log("check " +c.dateValue + '?=' + dt);
19353 if(c.dateValue == dt){
19363 findCells : function(ev) {
19364 var s = ev.start.clone().clearTime().getTime();
19366 var e= ev.end.clone().clearTime().getTime();
19369 this.cells.each(function(c){
19370 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19372 if(c.dateValue > e){
19375 if(c.dateValue < s){
19384 // findBestRow: function(cells)
19388 // for (var i =0 ; i < cells.length;i++) {
19389 // ret = Math.max(cells[i].rows || 0,ret);
19396 addItem : function(ev)
19398 // look for vertical location slot in
19399 var cells = this.findCells(ev);
19401 // ev.row = this.findBestRow(cells);
19403 // work out the location.
19407 for(var i =0; i < cells.length; i++) {
19409 cells[i].row = cells[0].row;
19412 cells[i].row = cells[i].row + 1;
19422 if (crow.start.getY() == cells[i].getY()) {
19424 crow.end = cells[i];
19441 cells[0].events.push(ev);
19443 this.calevents.push(ev);
19446 clearEvents: function() {
19448 if(!this.calevents){
19452 Roo.each(this.cells.elements, function(c){
19458 Roo.each(this.calevents, function(e) {
19459 Roo.each(e.els, function(el) {
19460 el.un('mouseenter' ,this.onEventEnter, this);
19461 el.un('mouseleave' ,this.onEventLeave, this);
19466 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19472 renderEvents: function()
19476 this.cells.each(function(c) {
19485 if(c.row != c.events.length){
19486 r = 4 - (4 - (c.row - c.events.length));
19489 c.events = ev.slice(0, r);
19490 c.more = ev.slice(r);
19492 if(c.more.length && c.more.length == 1){
19493 c.events.push(c.more.pop());
19496 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19500 this.cells.each(function(c) {
19502 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19505 for (var e = 0; e < c.events.length; e++){
19506 var ev = c.events[e];
19507 var rows = ev.rows;
19509 for(var i = 0; i < rows.length; i++) {
19511 // how many rows should it span..
19514 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19515 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19517 unselectable : "on",
19520 cls: 'fc-event-inner',
19524 // cls: 'fc-event-time',
19525 // html : cells.length > 1 ? '' : ev.time
19529 cls: 'fc-event-title',
19530 html : String.format('{0}', ev.title)
19537 cls: 'ui-resizable-handle ui-resizable-e',
19538 html : '  '
19545 cfg.cls += ' fc-event-start';
19547 if ((i+1) == rows.length) {
19548 cfg.cls += ' fc-event-end';
19551 var ctr = _this.el.select('.fc-event-container',true).first();
19552 var cg = ctr.createChild(cfg);
19554 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19555 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19557 var r = (c.more.length) ? 1 : 0;
19558 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19559 cg.setWidth(ebox.right - sbox.x -2);
19561 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19562 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19563 cg.on('click', _this.onEventClick, _this, ev);
19574 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19575 style : 'position: absolute',
19576 unselectable : "on",
19579 cls: 'fc-event-inner',
19583 cls: 'fc-event-title',
19591 cls: 'ui-resizable-handle ui-resizable-e',
19592 html : '  '
19598 var ctr = _this.el.select('.fc-event-container',true).first();
19599 var cg = ctr.createChild(cfg);
19601 var sbox = c.select('.fc-day-content',true).first().getBox();
19602 var ebox = c.select('.fc-day-content',true).first().getBox();
19604 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19605 cg.setWidth(ebox.right - sbox.x -2);
19607 cg.on('click', _this.onMoreEventClick, _this, c.more);
19617 onEventEnter: function (e, el,event,d) {
19618 this.fireEvent('evententer', this, el, event);
19621 onEventLeave: function (e, el,event,d) {
19622 this.fireEvent('eventleave', this, el, event);
19625 onEventClick: function (e, el,event,d) {
19626 this.fireEvent('eventclick', this, el, event);
19629 onMonthChange: function () {
19633 onMoreEventClick: function(e, el, more)
19637 this.calpopover.placement = 'right';
19638 this.calpopover.setTitle('More');
19640 this.calpopover.setContent('');
19642 var ctr = this.calpopover.el.select('.popover-content', true).first();
19644 Roo.each(more, function(m){
19646 cls : 'fc-event-hori fc-event-draggable',
19649 var cg = ctr.createChild(cfg);
19651 cg.on('click', _this.onEventClick, _this, m);
19654 this.calpopover.show(el);
19659 onLoad: function ()
19661 this.calevents = [];
19664 if(this.store.getCount() > 0){
19665 this.store.data.each(function(d){
19668 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19669 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19670 time : d.data.start_time,
19671 title : d.data.title,
19672 description : d.data.description,
19673 venue : d.data.venue
19678 this.renderEvents();
19680 if(this.calevents.length && this.loadMask){
19681 this.maskEl.hide();
19685 onBeforeLoad: function()
19687 this.clearEvents();
19689 this.maskEl.show();
19703 * @class Roo.bootstrap.Popover
19704 * @extends Roo.bootstrap.Component
19705 * Bootstrap Popover class
19706 * @cfg {String} html contents of the popover (or false to use children..)
19707 * @cfg {String} title of popover (or false to hide)
19708 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19709 * @cfg {String} trigger click || hover (or false to trigger manually)
19710 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19711 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19712 * - if false and it has a 'parent' then it will be automatically added to that element
19713 * - if string - Roo.get will be called
19714 * @cfg {Number} delay - delay before showing
19717 * Create a new Popover
19718 * @param {Object} config The config object
19721 Roo.bootstrap.Popover = function(config){
19722 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19728 * After the popover show
19730 * @param {Roo.bootstrap.Popover} this
19735 * After the popover hide
19737 * @param {Roo.bootstrap.Popover} this
19743 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19748 placement : 'right',
19749 trigger : 'hover', // hover
19755 can_build_overlaid : false,
19757 maskEl : false, // the mask element
19760 alignEl : false, // when show is called with an element - this get's stored.
19762 getChildContainer : function()
19764 return this.contentEl;
19767 getPopoverHeader : function()
19769 this.title = true; // flag not to hide it..
19770 this.headerEl.addClass('p-0');
19771 return this.headerEl
19775 getAutoCreate : function(){
19778 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19779 style: 'display:block',
19785 cls : 'popover-inner ',
19789 cls: 'popover-title popover-header',
19790 html : this.title === false ? '' : this.title
19793 cls : 'popover-content popover-body ' + (this.cls || ''),
19794 html : this.html || ''
19805 * @param {string} the title
19807 setTitle: function(str)
19811 this.headerEl.dom.innerHTML = str;
19816 * @param {string} the body content
19818 setContent: function(str)
19821 if (this.contentEl) {
19822 this.contentEl.dom.innerHTML = str;
19826 // as it get's added to the bottom of the page.
19827 onRender : function(ct, position)
19829 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19834 var cfg = Roo.apply({}, this.getAutoCreate());
19838 cfg.cls += ' ' + this.cls;
19841 cfg.style = this.style;
19843 //Roo.log("adding to ");
19844 this.el = Roo.get(document.body).createChild(cfg, position);
19845 // Roo.log(this.el);
19848 this.contentEl = this.el.select('.popover-content',true).first();
19849 this.headerEl = this.el.select('.popover-title',true).first();
19852 if(typeof(this.items) != 'undefined'){
19853 var items = this.items;
19856 for(var i =0;i < items.length;i++) {
19857 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19861 this.items = nitems;
19863 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19864 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19871 resizeMask : function()
19873 this.maskEl.setSize(
19874 Roo.lib.Dom.getViewWidth(true),
19875 Roo.lib.Dom.getViewHeight(true)
19879 initEvents : function()
19883 Roo.bootstrap.Popover.register(this);
19886 this.arrowEl = this.el.select('.arrow',true).first();
19887 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19888 this.el.enableDisplayMode('block');
19892 if (this.over === false && !this.parent()) {
19895 if (this.triggers === false) {
19900 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19901 var triggers = this.trigger ? this.trigger.split(' ') : [];
19902 Roo.each(triggers, function(trigger) {
19904 if (trigger == 'click') {
19905 on_el.on('click', this.toggle, this);
19906 } else if (trigger != 'manual') {
19907 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19908 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19910 on_el.on(eventIn ,this.enter, this);
19911 on_el.on(eventOut, this.leave, this);
19921 toggle : function () {
19922 this.hoverState == 'in' ? this.leave() : this.enter();
19925 enter : function () {
19927 clearTimeout(this.timeout);
19929 this.hoverState = 'in';
19931 if (!this.delay || !this.delay.show) {
19936 this.timeout = setTimeout(function () {
19937 if (_t.hoverState == 'in') {
19940 }, this.delay.show)
19943 leave : function() {
19944 clearTimeout(this.timeout);
19946 this.hoverState = 'out';
19948 if (!this.delay || !this.delay.hide) {
19953 this.timeout = setTimeout(function () {
19954 if (_t.hoverState == 'out') {
19957 }, this.delay.hide)
19961 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19962 * @param {string} (left|right|top|bottom) position
19964 show : function (on_el, placement)
19966 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19967 on_el = on_el || false; // default to false
19970 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19971 on_el = this.parent().el;
19972 } else if (this.over) {
19973 Roo.get(this.over);
19978 this.alignEl = Roo.get( on_el );
19981 this.render(document.body);
19987 if (this.title === false) {
19988 this.headerEl.hide();
19993 this.el.dom.style.display = 'block';
19996 if (this.alignEl) {
19997 this.updatePosition(this.placement, true);
20000 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20001 var es = this.el.getSize();
20002 var x = Roo.lib.Dom.getViewWidth()/2;
20003 var y = Roo.lib.Dom.getViewHeight()/2;
20004 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20009 //var arrow = this.el.select('.arrow',true).first();
20010 //arrow.set(align[2],
20012 this.el.addClass('in');
20016 this.hoverState = 'in';
20019 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20020 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20021 this.maskEl.dom.style.display = 'block';
20022 this.maskEl.addClass('show');
20024 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20026 this.fireEvent('show', this);
20030 * fire this manually after loading a grid in the table for example
20031 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20032 * @param {Boolean} try and move it if we cant get right position.
20034 updatePosition : function(placement, try_move)
20036 // allow for calling with no parameters
20037 placement = placement ? placement : this.placement;
20038 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20040 this.el.removeClass([
20041 'fade','top','bottom', 'left', 'right','in',
20042 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20044 this.el.addClass(placement + ' bs-popover-' + placement);
20046 if (!this.alignEl ) {
20050 switch (placement) {
20052 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20053 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20054 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20055 //normal display... or moved up/down.
20056 this.el.setXY(offset);
20057 var xy = this.alignEl.getAnchorXY('tr', false);
20059 this.arrowEl.setXY(xy);
20062 // continue through...
20063 return this.updatePosition('left', false);
20067 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20068 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20069 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20070 //normal display... or moved up/down.
20071 this.el.setXY(offset);
20072 var xy = this.alignEl.getAnchorXY('tl', false);
20073 xy[0]-=10;xy[1]+=5; // << fix me
20074 this.arrowEl.setXY(xy);
20078 return this.updatePosition('right', false);
20081 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20082 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20083 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20084 //normal display... or moved up/down.
20085 this.el.setXY(offset);
20086 var xy = this.alignEl.getAnchorXY('t', false);
20087 xy[1]-=10; // << fix me
20088 this.arrowEl.setXY(xy);
20092 return this.updatePosition('bottom', false);
20095 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20096 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20097 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20098 //normal display... or moved up/down.
20099 this.el.setXY(offset);
20100 var xy = this.alignEl.getAnchorXY('b', false);
20101 xy[1]+=2; // << fix me
20102 this.arrowEl.setXY(xy);
20106 return this.updatePosition('top', false);
20117 this.el.setXY([0,0]);
20118 this.el.removeClass('in');
20120 this.hoverState = null;
20121 this.maskEl.hide(); // always..
20122 this.fireEvent('hide', this);
20128 Roo.apply(Roo.bootstrap.Popover, {
20131 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20132 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20133 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20134 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20139 clickHander : false,
20142 onMouseDown : function(e)
20144 if (!e.getTarget(".roo-popover")) {
20152 register : function(popup)
20154 if (!Roo.bootstrap.Popover.clickHandler) {
20155 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20157 // hide other popups.
20159 this.popups.push(popup);
20161 hideAll : function()
20163 this.popups.forEach(function(p) {
20171 * Card header - holder for the card header elements.
20176 * @class Roo.bootstrap.PopoverNav
20177 * @extends Roo.bootstrap.NavGroup
20178 * Bootstrap Popover header navigation class
20180 * Create a new Popover Header Navigation
20181 * @param {Object} config The config object
20184 Roo.bootstrap.PopoverNav = function(config){
20185 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20188 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20191 container_method : 'getPopoverHeader'
20209 * @class Roo.bootstrap.Progress
20210 * @extends Roo.bootstrap.Component
20211 * Bootstrap Progress class
20212 * @cfg {Boolean} striped striped of the progress bar
20213 * @cfg {Boolean} active animated of the progress bar
20217 * Create a new Progress
20218 * @param {Object} config The config object
20221 Roo.bootstrap.Progress = function(config){
20222 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20225 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20230 getAutoCreate : function(){
20238 cfg.cls += ' progress-striped';
20242 cfg.cls += ' active';
20261 * @class Roo.bootstrap.ProgressBar
20262 * @extends Roo.bootstrap.Component
20263 * Bootstrap ProgressBar class
20264 * @cfg {Number} aria_valuenow aria-value now
20265 * @cfg {Number} aria_valuemin aria-value min
20266 * @cfg {Number} aria_valuemax aria-value max
20267 * @cfg {String} label label for the progress bar
20268 * @cfg {String} panel (success | info | warning | danger )
20269 * @cfg {String} role role of the progress bar
20270 * @cfg {String} sr_only text
20274 * Create a new ProgressBar
20275 * @param {Object} config The config object
20278 Roo.bootstrap.ProgressBar = function(config){
20279 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20282 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20286 aria_valuemax : 100,
20292 getAutoCreate : function()
20297 cls: 'progress-bar',
20298 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20310 cfg.role = this.role;
20313 if(this.aria_valuenow){
20314 cfg['aria-valuenow'] = this.aria_valuenow;
20317 if(this.aria_valuemin){
20318 cfg['aria-valuemin'] = this.aria_valuemin;
20321 if(this.aria_valuemax){
20322 cfg['aria-valuemax'] = this.aria_valuemax;
20325 if(this.label && !this.sr_only){
20326 cfg.html = this.label;
20330 cfg.cls += ' progress-bar-' + this.panel;
20336 update : function(aria_valuenow)
20338 this.aria_valuenow = aria_valuenow;
20340 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20355 * @class Roo.bootstrap.TabGroup
20356 * @extends Roo.bootstrap.Column
20357 * Bootstrap Column class
20358 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20359 * @cfg {Boolean} carousel true to make the group behave like a carousel
20360 * @cfg {Boolean} bullets show bullets for the panels
20361 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20362 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20363 * @cfg {Boolean} showarrow (true|false) show arrow default true
20366 * Create a new TabGroup
20367 * @param {Object} config The config object
20370 Roo.bootstrap.TabGroup = function(config){
20371 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20373 this.navId = Roo.id();
20376 Roo.bootstrap.TabGroup.register(this);
20380 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20383 transition : false,
20388 slideOnTouch : false,
20391 getAutoCreate : function()
20393 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20395 cfg.cls += ' tab-content';
20397 if (this.carousel) {
20398 cfg.cls += ' carousel slide';
20401 cls : 'carousel-inner',
20405 if(this.bullets && !Roo.isTouch){
20408 cls : 'carousel-bullets',
20412 if(this.bullets_cls){
20413 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20420 cfg.cn[0].cn.push(bullets);
20423 if(this.showarrow){
20424 cfg.cn[0].cn.push({
20426 class : 'carousel-arrow',
20430 class : 'carousel-prev',
20434 class : 'fa fa-chevron-left'
20440 class : 'carousel-next',
20444 class : 'fa fa-chevron-right'
20457 initEvents: function()
20459 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20460 // this.el.on("touchstart", this.onTouchStart, this);
20463 if(this.autoslide){
20466 this.slideFn = window.setInterval(function() {
20467 _this.showPanelNext();
20471 if(this.showarrow){
20472 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20473 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20479 // onTouchStart : function(e, el, o)
20481 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20485 // this.showPanelNext();
20489 getChildContainer : function()
20491 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20495 * register a Navigation item
20496 * @param {Roo.bootstrap.NavItem} the navitem to add
20498 register : function(item)
20500 this.tabs.push( item);
20501 item.navId = this.navId; // not really needed..
20506 getActivePanel : function()
20509 Roo.each(this.tabs, function(t) {
20519 getPanelByName : function(n)
20522 Roo.each(this.tabs, function(t) {
20523 if (t.tabId == n) {
20531 indexOfPanel : function(p)
20534 Roo.each(this.tabs, function(t,i) {
20535 if (t.tabId == p.tabId) {
20544 * show a specific panel
20545 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20546 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20548 showPanel : function (pan)
20550 if(this.transition || typeof(pan) == 'undefined'){
20551 Roo.log("waiting for the transitionend");
20555 if (typeof(pan) == 'number') {
20556 pan = this.tabs[pan];
20559 if (typeof(pan) == 'string') {
20560 pan = this.getPanelByName(pan);
20563 var cur = this.getActivePanel();
20566 Roo.log('pan or acitve pan is undefined');
20570 if (pan.tabId == this.getActivePanel().tabId) {
20574 if (false === cur.fireEvent('beforedeactivate')) {
20578 if(this.bullets > 0 && !Roo.isTouch){
20579 this.setActiveBullet(this.indexOfPanel(pan));
20582 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20584 //class="carousel-item carousel-item-next carousel-item-left"
20586 this.transition = true;
20587 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20588 var lr = dir == 'next' ? 'left' : 'right';
20589 pan.el.addClass(dir); // or prev
20590 pan.el.addClass('carousel-item-' + dir); // or prev
20591 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20592 cur.el.addClass(lr); // or right
20593 pan.el.addClass(lr);
20594 cur.el.addClass('carousel-item-' +lr); // or right
20595 pan.el.addClass('carousel-item-' +lr);
20599 cur.el.on('transitionend', function() {
20600 Roo.log("trans end?");
20602 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20603 pan.setActive(true);
20605 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20606 cur.setActive(false);
20608 _this.transition = false;
20610 }, this, { single: true } );
20615 cur.setActive(false);
20616 pan.setActive(true);
20621 showPanelNext : function()
20623 var i = this.indexOfPanel(this.getActivePanel());
20625 if (i >= this.tabs.length - 1 && !this.autoslide) {
20629 if (i >= this.tabs.length - 1 && this.autoslide) {
20633 this.showPanel(this.tabs[i+1]);
20636 showPanelPrev : function()
20638 var i = this.indexOfPanel(this.getActivePanel());
20640 if (i < 1 && !this.autoslide) {
20644 if (i < 1 && this.autoslide) {
20645 i = this.tabs.length;
20648 this.showPanel(this.tabs[i-1]);
20652 addBullet: function()
20654 if(!this.bullets || Roo.isTouch){
20657 var ctr = this.el.select('.carousel-bullets',true).first();
20658 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20659 var bullet = ctr.createChild({
20660 cls : 'bullet bullet-' + i
20661 },ctr.dom.lastChild);
20666 bullet.on('click', (function(e, el, o, ii, t){
20668 e.preventDefault();
20670 this.showPanel(ii);
20672 if(this.autoslide && this.slideFn){
20673 clearInterval(this.slideFn);
20674 this.slideFn = window.setInterval(function() {
20675 _this.showPanelNext();
20679 }).createDelegate(this, [i, bullet], true));
20684 setActiveBullet : function(i)
20690 Roo.each(this.el.select('.bullet', true).elements, function(el){
20691 el.removeClass('selected');
20694 var bullet = this.el.select('.bullet-' + i, true).first();
20700 bullet.addClass('selected');
20711 Roo.apply(Roo.bootstrap.TabGroup, {
20715 * register a Navigation Group
20716 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20718 register : function(navgrp)
20720 this.groups[navgrp.navId] = navgrp;
20724 * fetch a Navigation Group based on the navigation ID
20725 * if one does not exist , it will get created.
20726 * @param {string} the navgroup to add
20727 * @returns {Roo.bootstrap.NavGroup} the navgroup
20729 get: function(navId) {
20730 if (typeof(this.groups[navId]) == 'undefined') {
20731 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20733 return this.groups[navId] ;
20748 * @class Roo.bootstrap.TabPanel
20749 * @extends Roo.bootstrap.Component
20750 * Bootstrap TabPanel class
20751 * @cfg {Boolean} active panel active
20752 * @cfg {String} html panel content
20753 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20754 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20755 * @cfg {String} href click to link..
20756 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20760 * Create a new TabPanel
20761 * @param {Object} config The config object
20764 Roo.bootstrap.TabPanel = function(config){
20765 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20769 * Fires when the active status changes
20770 * @param {Roo.bootstrap.TabPanel} this
20771 * @param {Boolean} state the new state
20776 * @event beforedeactivate
20777 * Fires before a tab is de-activated - can be used to do validation on a form.
20778 * @param {Roo.bootstrap.TabPanel} this
20779 * @return {Boolean} false if there is an error
20782 'beforedeactivate': true
20785 this.tabId = this.tabId || Roo.id();
20789 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20796 touchSlide : false,
20797 getAutoCreate : function(){
20802 // item is needed for carousel - not sure if it has any effect otherwise
20803 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20804 html: this.html || ''
20808 cfg.cls += ' active';
20812 cfg.tabId = this.tabId;
20820 initEvents: function()
20822 var p = this.parent();
20824 this.navId = this.navId || p.navId;
20826 if (typeof(this.navId) != 'undefined') {
20827 // not really needed.. but just in case.. parent should be a NavGroup.
20828 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20832 var i = tg.tabs.length - 1;
20834 if(this.active && tg.bullets > 0 && i < tg.bullets){
20835 tg.setActiveBullet(i);
20839 this.el.on('click', this.onClick, this);
20841 if(Roo.isTouch && this.touchSlide){
20842 this.el.on("touchstart", this.onTouchStart, this);
20843 this.el.on("touchmove", this.onTouchMove, this);
20844 this.el.on("touchend", this.onTouchEnd, this);
20849 onRender : function(ct, position)
20851 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20854 setActive : function(state)
20856 Roo.log("panel - set active " + this.tabId + "=" + state);
20858 this.active = state;
20860 this.el.removeClass('active');
20862 } else if (!this.el.hasClass('active')) {
20863 this.el.addClass('active');
20866 this.fireEvent('changed', this, state);
20869 onClick : function(e)
20871 e.preventDefault();
20873 if(!this.href.length){
20877 window.location.href = this.href;
20886 onTouchStart : function(e)
20888 this.swiping = false;
20890 this.startX = e.browserEvent.touches[0].clientX;
20891 this.startY = e.browserEvent.touches[0].clientY;
20894 onTouchMove : function(e)
20896 this.swiping = true;
20898 this.endX = e.browserEvent.touches[0].clientX;
20899 this.endY = e.browserEvent.touches[0].clientY;
20902 onTouchEnd : function(e)
20909 var tabGroup = this.parent();
20911 if(this.endX > this.startX){ // swiping right
20912 tabGroup.showPanelPrev();
20916 if(this.startX > this.endX){ // swiping left
20917 tabGroup.showPanelNext();
20936 * @class Roo.bootstrap.DateField
20937 * @extends Roo.bootstrap.Input
20938 * Bootstrap DateField class
20939 * @cfg {Number} weekStart default 0
20940 * @cfg {String} viewMode default empty, (months|years)
20941 * @cfg {String} minViewMode default empty, (months|years)
20942 * @cfg {Number} startDate default -Infinity
20943 * @cfg {Number} endDate default Infinity
20944 * @cfg {Boolean} todayHighlight default false
20945 * @cfg {Boolean} todayBtn default false
20946 * @cfg {Boolean} calendarWeeks default false
20947 * @cfg {Object} daysOfWeekDisabled default empty
20948 * @cfg {Boolean} singleMode default false (true | false)
20950 * @cfg {Boolean} keyboardNavigation default true
20951 * @cfg {String} language default en
20954 * Create a new DateField
20955 * @param {Object} config The config object
20958 Roo.bootstrap.DateField = function(config){
20959 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20963 * Fires when this field show.
20964 * @param {Roo.bootstrap.DateField} this
20965 * @param {Mixed} date The date value
20970 * Fires when this field hide.
20971 * @param {Roo.bootstrap.DateField} this
20972 * @param {Mixed} date The date value
20977 * Fires when select a date.
20978 * @param {Roo.bootstrap.DateField} this
20979 * @param {Mixed} date The date value
20983 * @event beforeselect
20984 * Fires when before select a date.
20985 * @param {Roo.bootstrap.DateField} this
20986 * @param {Mixed} date The date value
20988 beforeselect : true
20992 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20995 * @cfg {String} format
20996 * The default date format string which can be overriden for localization support. The format must be
20997 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21001 * @cfg {String} altFormats
21002 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21003 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21005 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21013 todayHighlight : false,
21019 keyboardNavigation: true,
21021 calendarWeeks: false,
21023 startDate: -Infinity,
21027 daysOfWeekDisabled: [],
21031 singleMode : false,
21033 UTCDate: function()
21035 return new Date(Date.UTC.apply(Date, arguments));
21038 UTCToday: function()
21040 var today = new Date();
21041 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21044 getDate: function() {
21045 var d = this.getUTCDate();
21046 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21049 getUTCDate: function() {
21053 setDate: function(d) {
21054 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21057 setUTCDate: function(d) {
21059 this.setValue(this.formatDate(this.date));
21062 onRender: function(ct, position)
21065 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21067 this.language = this.language || 'en';
21068 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21069 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21071 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21072 this.format = this.format || 'm/d/y';
21073 this.isInline = false;
21074 this.isInput = true;
21075 this.component = this.el.select('.add-on', true).first() || false;
21076 this.component = (this.component && this.component.length === 0) ? false : this.component;
21077 this.hasInput = this.component && this.inputEl().length;
21079 if (typeof(this.minViewMode === 'string')) {
21080 switch (this.minViewMode) {
21082 this.minViewMode = 1;
21085 this.minViewMode = 2;
21088 this.minViewMode = 0;
21093 if (typeof(this.viewMode === 'string')) {
21094 switch (this.viewMode) {
21107 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21109 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21111 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21113 this.picker().on('mousedown', this.onMousedown, this);
21114 this.picker().on('click', this.onClick, this);
21116 this.picker().addClass('datepicker-dropdown');
21118 this.startViewMode = this.viewMode;
21120 if(this.singleMode){
21121 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21122 v.setVisibilityMode(Roo.Element.DISPLAY);
21126 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21127 v.setStyle('width', '189px');
21131 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21132 if(!this.calendarWeeks){
21137 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21138 v.attr('colspan', function(i, val){
21139 return parseInt(val) + 1;
21144 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21146 this.setStartDate(this.startDate);
21147 this.setEndDate(this.endDate);
21149 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21156 if(this.isInline) {
21161 picker : function()
21163 return this.pickerEl;
21164 // return this.el.select('.datepicker', true).first();
21167 fillDow: function()
21169 var dowCnt = this.weekStart;
21178 if(this.calendarWeeks){
21186 while (dowCnt < this.weekStart + 7) {
21190 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21194 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21197 fillMonths: function()
21200 var months = this.picker().select('>.datepicker-months td', true).first();
21202 months.dom.innerHTML = '';
21208 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21211 months.createChild(month);
21218 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;
21220 if (this.date < this.startDate) {
21221 this.viewDate = new Date(this.startDate);
21222 } else if (this.date > this.endDate) {
21223 this.viewDate = new Date(this.endDate);
21225 this.viewDate = new Date(this.date);
21233 var d = new Date(this.viewDate),
21234 year = d.getUTCFullYear(),
21235 month = d.getUTCMonth(),
21236 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21237 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21238 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21239 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21240 currentDate = this.date && this.date.valueOf(),
21241 today = this.UTCToday();
21243 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21245 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21247 // this.picker.select('>tfoot th.today').
21248 // .text(dates[this.language].today)
21249 // .toggle(this.todayBtn !== false);
21251 this.updateNavArrows();
21254 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21256 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21258 prevMonth.setUTCDate(day);
21260 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21262 var nextMonth = new Date(prevMonth);
21264 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21266 nextMonth = nextMonth.valueOf();
21268 var fillMonths = false;
21270 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21272 while(prevMonth.valueOf() <= nextMonth) {
21275 if (prevMonth.getUTCDay() === this.weekStart) {
21277 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21285 if(this.calendarWeeks){
21286 // ISO 8601: First week contains first thursday.
21287 // ISO also states week starts on Monday, but we can be more abstract here.
21289 // Start of current week: based on weekstart/current date
21290 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21291 // Thursday of this week
21292 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21293 // First Thursday of year, year from thursday
21294 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21295 // Calendar week: ms between thursdays, div ms per day, div 7 days
21296 calWeek = (th - yth) / 864e5 / 7 + 1;
21298 fillMonths.cn.push({
21306 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21308 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21311 if (this.todayHighlight &&
21312 prevMonth.getUTCFullYear() == today.getFullYear() &&
21313 prevMonth.getUTCMonth() == today.getMonth() &&
21314 prevMonth.getUTCDate() == today.getDate()) {
21315 clsName += ' today';
21318 if (currentDate && prevMonth.valueOf() === currentDate) {
21319 clsName += ' active';
21322 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21323 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21324 clsName += ' disabled';
21327 fillMonths.cn.push({
21329 cls: 'day ' + clsName,
21330 html: prevMonth.getDate()
21333 prevMonth.setDate(prevMonth.getDate()+1);
21336 var currentYear = this.date && this.date.getUTCFullYear();
21337 var currentMonth = this.date && this.date.getUTCMonth();
21339 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21341 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21342 v.removeClass('active');
21344 if(currentYear === year && k === currentMonth){
21345 v.addClass('active');
21348 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21349 v.addClass('disabled');
21355 year = parseInt(year/10, 10) * 10;
21357 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21359 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21362 for (var i = -1; i < 11; i++) {
21363 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21365 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21373 showMode: function(dir)
21376 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21379 Roo.each(this.picker().select('>div',true).elements, function(v){
21380 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21383 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21388 if(this.isInline) {
21392 this.picker().removeClass(['bottom', 'top']);
21394 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21396 * place to the top of element!
21400 this.picker().addClass('top');
21401 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21406 this.picker().addClass('bottom');
21408 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21411 parseDate : function(value)
21413 if(!value || value instanceof Date){
21416 var v = Date.parseDate(value, this.format);
21417 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21418 v = Date.parseDate(value, 'Y-m-d');
21420 if(!v && this.altFormats){
21421 if(!this.altFormatsArray){
21422 this.altFormatsArray = this.altFormats.split("|");
21424 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21425 v = Date.parseDate(value, this.altFormatsArray[i]);
21431 formatDate : function(date, fmt)
21433 return (!date || !(date instanceof Date)) ?
21434 date : date.dateFormat(fmt || this.format);
21437 onFocus : function()
21439 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21443 onBlur : function()
21445 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21447 var d = this.inputEl().getValue();
21454 showPopup : function()
21456 this.picker().show();
21460 this.fireEvent('showpopup', this, this.date);
21463 hidePopup : function()
21465 if(this.isInline) {
21468 this.picker().hide();
21469 this.viewMode = this.startViewMode;
21472 this.fireEvent('hidepopup', this, this.date);
21476 onMousedown: function(e)
21478 e.stopPropagation();
21479 e.preventDefault();
21484 Roo.bootstrap.DateField.superclass.keyup.call(this);
21488 setValue: function(v)
21490 if(this.fireEvent('beforeselect', this, v) !== false){
21491 var d = new Date(this.parseDate(v) ).clearTime();
21493 if(isNaN(d.getTime())){
21494 this.date = this.viewDate = '';
21495 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21499 v = this.formatDate(d);
21501 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21503 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21507 this.fireEvent('select', this, this.date);
21511 getValue: function()
21513 return this.formatDate(this.date);
21516 fireKey: function(e)
21518 if (!this.picker().isVisible()){
21519 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21525 var dateChanged = false,
21527 newDate, newViewDate;
21532 e.preventDefault();
21536 if (!this.keyboardNavigation) {
21539 dir = e.keyCode == 37 ? -1 : 1;
21542 newDate = this.moveYear(this.date, dir);
21543 newViewDate = this.moveYear(this.viewDate, dir);
21544 } else if (e.shiftKey){
21545 newDate = this.moveMonth(this.date, dir);
21546 newViewDate = this.moveMonth(this.viewDate, dir);
21548 newDate = new Date(this.date);
21549 newDate.setUTCDate(this.date.getUTCDate() + dir);
21550 newViewDate = new Date(this.viewDate);
21551 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21553 if (this.dateWithinRange(newDate)){
21554 this.date = newDate;
21555 this.viewDate = newViewDate;
21556 this.setValue(this.formatDate(this.date));
21558 e.preventDefault();
21559 dateChanged = true;
21564 if (!this.keyboardNavigation) {
21567 dir = e.keyCode == 38 ? -1 : 1;
21569 newDate = this.moveYear(this.date, dir);
21570 newViewDate = this.moveYear(this.viewDate, dir);
21571 } else if (e.shiftKey){
21572 newDate = this.moveMonth(this.date, dir);
21573 newViewDate = this.moveMonth(this.viewDate, dir);
21575 newDate = new Date(this.date);
21576 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21577 newViewDate = new Date(this.viewDate);
21578 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21580 if (this.dateWithinRange(newDate)){
21581 this.date = newDate;
21582 this.viewDate = newViewDate;
21583 this.setValue(this.formatDate(this.date));
21585 e.preventDefault();
21586 dateChanged = true;
21590 this.setValue(this.formatDate(this.date));
21592 e.preventDefault();
21595 this.setValue(this.formatDate(this.date));
21609 onClick: function(e)
21611 e.stopPropagation();
21612 e.preventDefault();
21614 var target = e.getTarget();
21616 if(target.nodeName.toLowerCase() === 'i'){
21617 target = Roo.get(target).dom.parentNode;
21620 var nodeName = target.nodeName;
21621 var className = target.className;
21622 var html = target.innerHTML;
21623 //Roo.log(nodeName);
21625 switch(nodeName.toLowerCase()) {
21627 switch(className) {
21633 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21634 switch(this.viewMode){
21636 this.viewDate = this.moveMonth(this.viewDate, dir);
21640 this.viewDate = this.moveYear(this.viewDate, dir);
21646 var date = new Date();
21647 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21649 this.setValue(this.formatDate(this.date));
21656 if (className.indexOf('disabled') < 0) {
21657 this.viewDate.setUTCDate(1);
21658 if (className.indexOf('month') > -1) {
21659 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21661 var year = parseInt(html, 10) || 0;
21662 this.viewDate.setUTCFullYear(year);
21666 if(this.singleMode){
21667 this.setValue(this.formatDate(this.viewDate));
21678 //Roo.log(className);
21679 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21680 var day = parseInt(html, 10) || 1;
21681 var year = (this.viewDate || new Date()).getUTCFullYear(),
21682 month = (this.viewDate || new Date()).getUTCMonth();
21684 if (className.indexOf('old') > -1) {
21691 } else if (className.indexOf('new') > -1) {
21699 //Roo.log([year,month,day]);
21700 this.date = this.UTCDate(year, month, day,0,0,0,0);
21701 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21703 //Roo.log(this.formatDate(this.date));
21704 this.setValue(this.formatDate(this.date));
21711 setStartDate: function(startDate)
21713 this.startDate = startDate || -Infinity;
21714 if (this.startDate !== -Infinity) {
21715 this.startDate = this.parseDate(this.startDate);
21718 this.updateNavArrows();
21721 setEndDate: function(endDate)
21723 this.endDate = endDate || Infinity;
21724 if (this.endDate !== Infinity) {
21725 this.endDate = this.parseDate(this.endDate);
21728 this.updateNavArrows();
21731 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21733 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21734 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21735 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21737 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21738 return parseInt(d, 10);
21741 this.updateNavArrows();
21744 updateNavArrows: function()
21746 if(this.singleMode){
21750 var d = new Date(this.viewDate),
21751 year = d.getUTCFullYear(),
21752 month = d.getUTCMonth();
21754 Roo.each(this.picker().select('.prev', true).elements, function(v){
21756 switch (this.viewMode) {
21759 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21765 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21772 Roo.each(this.picker().select('.next', true).elements, function(v){
21774 switch (this.viewMode) {
21777 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21783 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21791 moveMonth: function(date, dir)
21796 var new_date = new Date(date.valueOf()),
21797 day = new_date.getUTCDate(),
21798 month = new_date.getUTCMonth(),
21799 mag = Math.abs(dir),
21801 dir = dir > 0 ? 1 : -1;
21804 // If going back one month, make sure month is not current month
21805 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21807 return new_date.getUTCMonth() == month;
21809 // If going forward one month, make sure month is as expected
21810 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21812 return new_date.getUTCMonth() != new_month;
21814 new_month = month + dir;
21815 new_date.setUTCMonth(new_month);
21816 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21817 if (new_month < 0 || new_month > 11) {
21818 new_month = (new_month + 12) % 12;
21821 // For magnitudes >1, move one month at a time...
21822 for (var i=0; i<mag; i++) {
21823 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21824 new_date = this.moveMonth(new_date, dir);
21826 // ...then reset the day, keeping it in the new month
21827 new_month = new_date.getUTCMonth();
21828 new_date.setUTCDate(day);
21830 return new_month != new_date.getUTCMonth();
21833 // Common date-resetting loop -- if date is beyond end of month, make it
21836 new_date.setUTCDate(--day);
21837 new_date.setUTCMonth(new_month);
21842 moveYear: function(date, dir)
21844 return this.moveMonth(date, dir*12);
21847 dateWithinRange: function(date)
21849 return date >= this.startDate && date <= this.endDate;
21855 this.picker().remove();
21858 validateValue : function(value)
21860 if(this.getVisibilityEl().hasClass('hidden')){
21864 if(value.length < 1) {
21865 if(this.allowBlank){
21871 if(value.length < this.minLength){
21874 if(value.length > this.maxLength){
21878 var vt = Roo.form.VTypes;
21879 if(!vt[this.vtype](value, this)){
21883 if(typeof this.validator == "function"){
21884 var msg = this.validator(value);
21890 if(this.regex && !this.regex.test(value)){
21894 if(typeof(this.parseDate(value)) == 'undefined'){
21898 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21902 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21912 this.date = this.viewDate = '';
21914 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21919 Roo.apply(Roo.bootstrap.DateField, {
21930 html: '<i class="fa fa-arrow-left"/>'
21940 html: '<i class="fa fa-arrow-right"/>'
21982 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21983 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21984 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21985 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21986 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21999 navFnc: 'FullYear',
22004 navFnc: 'FullYear',
22009 Roo.apply(Roo.bootstrap.DateField, {
22013 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22017 cls: 'datepicker-days',
22021 cls: 'table-condensed',
22023 Roo.bootstrap.DateField.head,
22027 Roo.bootstrap.DateField.footer
22034 cls: 'datepicker-months',
22038 cls: 'table-condensed',
22040 Roo.bootstrap.DateField.head,
22041 Roo.bootstrap.DateField.content,
22042 Roo.bootstrap.DateField.footer
22049 cls: 'datepicker-years',
22053 cls: 'table-condensed',
22055 Roo.bootstrap.DateField.head,
22056 Roo.bootstrap.DateField.content,
22057 Roo.bootstrap.DateField.footer
22076 * @class Roo.bootstrap.TimeField
22077 * @extends Roo.bootstrap.Input
22078 * Bootstrap DateField class
22082 * Create a new TimeField
22083 * @param {Object} config The config object
22086 Roo.bootstrap.TimeField = function(config){
22087 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22091 * Fires when this field show.
22092 * @param {Roo.bootstrap.DateField} thisthis
22093 * @param {Mixed} date The date value
22098 * Fires when this field hide.
22099 * @param {Roo.bootstrap.DateField} this
22100 * @param {Mixed} date The date value
22105 * Fires when select a date.
22106 * @param {Roo.bootstrap.DateField} this
22107 * @param {Mixed} date The date value
22113 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22116 * @cfg {String} format
22117 * The default time format string which can be overriden for localization support. The format must be
22118 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22122 getAutoCreate : function()
22124 this.after = '<i class="fa far fa-clock"></i>';
22125 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22129 onRender: function(ct, position)
22132 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22134 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22136 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22138 this.pop = this.picker().select('>.datepicker-time',true).first();
22139 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22141 this.picker().on('mousedown', this.onMousedown, this);
22142 this.picker().on('click', this.onClick, this);
22144 this.picker().addClass('datepicker-dropdown');
22149 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22150 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22151 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22152 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22153 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22154 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22158 fireKey: function(e){
22159 if (!this.picker().isVisible()){
22160 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22166 e.preventDefault();
22174 this.onTogglePeriod();
22177 this.onIncrementMinutes();
22180 this.onDecrementMinutes();
22189 onClick: function(e) {
22190 e.stopPropagation();
22191 e.preventDefault();
22194 picker : function()
22196 return this.pickerEl;
22199 fillTime: function()
22201 var time = this.pop.select('tbody', true).first();
22203 time.dom.innerHTML = '';
22218 cls: 'hours-up fa fas fa-chevron-up'
22238 cls: 'minutes-up fa fas fa-chevron-up'
22259 cls: 'timepicker-hour',
22274 cls: 'timepicker-minute',
22289 cls: 'btn btn-primary period',
22311 cls: 'hours-down fa fas fa-chevron-down'
22331 cls: 'minutes-down fa fas fa-chevron-down'
22349 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22356 var hours = this.time.getHours();
22357 var minutes = this.time.getMinutes();
22370 hours = hours - 12;
22374 hours = '0' + hours;
22378 minutes = '0' + minutes;
22381 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22382 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22383 this.pop.select('button', true).first().dom.innerHTML = period;
22389 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22391 var cls = ['bottom'];
22393 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22400 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22404 //this.picker().setXY(20000,20000);
22405 this.picker().addClass(cls.join('-'));
22409 Roo.each(cls, function(c){
22414 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22415 //_this.picker().setTop(_this.inputEl().getHeight());
22419 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22421 //_this.picker().setTop(0 - _this.picker().getHeight());
22426 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22430 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22438 onFocus : function()
22440 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22444 onBlur : function()
22446 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22452 this.picker().show();
22457 this.fireEvent('show', this, this.date);
22462 this.picker().hide();
22465 this.fireEvent('hide', this, this.date);
22468 setTime : function()
22471 this.setValue(this.time.format(this.format));
22473 this.fireEvent('select', this, this.date);
22478 onMousedown: function(e){
22479 e.stopPropagation();
22480 e.preventDefault();
22483 onIncrementHours: function()
22485 Roo.log('onIncrementHours');
22486 this.time = this.time.add(Date.HOUR, 1);
22491 onDecrementHours: function()
22493 Roo.log('onDecrementHours');
22494 this.time = this.time.add(Date.HOUR, -1);
22498 onIncrementMinutes: function()
22500 Roo.log('onIncrementMinutes');
22501 this.time = this.time.add(Date.MINUTE, 1);
22505 onDecrementMinutes: function()
22507 Roo.log('onDecrementMinutes');
22508 this.time = this.time.add(Date.MINUTE, -1);
22512 onTogglePeriod: function()
22514 Roo.log('onTogglePeriod');
22515 this.time = this.time.add(Date.HOUR, 12);
22523 Roo.apply(Roo.bootstrap.TimeField, {
22527 cls: 'datepicker dropdown-menu',
22531 cls: 'datepicker-time',
22535 cls: 'table-condensed',
22564 cls: 'btn btn-info ok',
22592 * @class Roo.bootstrap.MonthField
22593 * @extends Roo.bootstrap.Input
22594 * Bootstrap MonthField class
22596 * @cfg {String} language default en
22599 * Create a new MonthField
22600 * @param {Object} config The config object
22603 Roo.bootstrap.MonthField = function(config){
22604 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22609 * Fires when this field show.
22610 * @param {Roo.bootstrap.MonthField} this
22611 * @param {Mixed} date The date value
22616 * Fires when this field hide.
22617 * @param {Roo.bootstrap.MonthField} this
22618 * @param {Mixed} date The date value
22623 * Fires when select a date.
22624 * @param {Roo.bootstrap.MonthField} this
22625 * @param {String} oldvalue The old value
22626 * @param {String} newvalue The new value
22632 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22634 onRender: function(ct, position)
22637 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22639 this.language = this.language || 'en';
22640 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22641 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22643 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22644 this.isInline = false;
22645 this.isInput = true;
22646 this.component = this.el.select('.add-on', true).first() || false;
22647 this.component = (this.component && this.component.length === 0) ? false : this.component;
22648 this.hasInput = this.component && this.inputEL().length;
22650 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22652 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22654 this.picker().on('mousedown', this.onMousedown, this);
22655 this.picker().on('click', this.onClick, this);
22657 this.picker().addClass('datepicker-dropdown');
22659 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22660 v.setStyle('width', '189px');
22667 if(this.isInline) {
22673 setValue: function(v, suppressEvent)
22675 var o = this.getValue();
22677 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22681 if(suppressEvent !== true){
22682 this.fireEvent('select', this, o, v);
22687 getValue: function()
22692 onClick: function(e)
22694 e.stopPropagation();
22695 e.preventDefault();
22697 var target = e.getTarget();
22699 if(target.nodeName.toLowerCase() === 'i'){
22700 target = Roo.get(target).dom.parentNode;
22703 var nodeName = target.nodeName;
22704 var className = target.className;
22705 var html = target.innerHTML;
22707 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22711 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22713 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22719 picker : function()
22721 return this.pickerEl;
22724 fillMonths: function()
22727 var months = this.picker().select('>.datepicker-months td', true).first();
22729 months.dom.innerHTML = '';
22735 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22738 months.createChild(month);
22747 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22748 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22751 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22752 e.removeClass('active');
22754 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22755 e.addClass('active');
22762 if(this.isInline) {
22766 this.picker().removeClass(['bottom', 'top']);
22768 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22770 * place to the top of element!
22774 this.picker().addClass('top');
22775 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22780 this.picker().addClass('bottom');
22782 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22785 onFocus : function()
22787 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22791 onBlur : function()
22793 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22795 var d = this.inputEl().getValue();
22804 this.picker().show();
22805 this.picker().select('>.datepicker-months', true).first().show();
22809 this.fireEvent('show', this, this.date);
22814 if(this.isInline) {
22817 this.picker().hide();
22818 this.fireEvent('hide', this, this.date);
22822 onMousedown: function(e)
22824 e.stopPropagation();
22825 e.preventDefault();
22830 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22834 fireKey: function(e)
22836 if (!this.picker().isVisible()){
22837 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22848 e.preventDefault();
22852 dir = e.keyCode == 37 ? -1 : 1;
22854 this.vIndex = this.vIndex + dir;
22856 if(this.vIndex < 0){
22860 if(this.vIndex > 11){
22864 if(isNaN(this.vIndex)){
22868 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22874 dir = e.keyCode == 38 ? -1 : 1;
22876 this.vIndex = this.vIndex + dir * 4;
22878 if(this.vIndex < 0){
22882 if(this.vIndex > 11){
22886 if(isNaN(this.vIndex)){
22890 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22895 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22896 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22900 e.preventDefault();
22903 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22904 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22920 this.picker().remove();
22925 Roo.apply(Roo.bootstrap.MonthField, {
22944 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22945 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22950 Roo.apply(Roo.bootstrap.MonthField, {
22954 cls: 'datepicker dropdown-menu roo-dynamic',
22958 cls: 'datepicker-months',
22962 cls: 'table-condensed',
22964 Roo.bootstrap.DateField.content
22984 * @class Roo.bootstrap.CheckBox
22985 * @extends Roo.bootstrap.Input
22986 * Bootstrap CheckBox class
22988 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22989 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22990 * @cfg {String} boxLabel The text that appears beside the checkbox
22991 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22992 * @cfg {Boolean} checked initnal the element
22993 * @cfg {Boolean} inline inline the element (default false)
22994 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22995 * @cfg {String} tooltip label tooltip
22998 * Create a new CheckBox
22999 * @param {Object} config The config object
23002 Roo.bootstrap.CheckBox = function(config){
23003 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23008 * Fires when the element is checked or unchecked.
23009 * @param {Roo.bootstrap.CheckBox} this This input
23010 * @param {Boolean} checked The new checked value
23015 * Fires when the element is click.
23016 * @param {Roo.bootstrap.CheckBox} this This input
23023 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23025 inputType: 'checkbox',
23034 // checkbox success does not make any sense really..
23039 getAutoCreate : function()
23041 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23047 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23050 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23056 type : this.inputType,
23057 value : this.inputValue,
23058 cls : 'roo-' + this.inputType, //'form-box',
23059 placeholder : this.placeholder || ''
23063 if(this.inputType != 'radio'){
23067 cls : 'roo-hidden-value',
23068 value : this.checked ? this.inputValue : this.valueOff
23073 if (this.weight) { // Validity check?
23074 cfg.cls += " " + this.inputType + "-" + this.weight;
23077 if (this.disabled) {
23078 input.disabled=true;
23082 input.checked = this.checked;
23087 input.name = this.name;
23089 if(this.inputType != 'radio'){
23090 hidden.name = this.name;
23091 input.name = '_hidden_' + this.name;
23096 input.cls += ' input-' + this.size;
23101 ['xs','sm','md','lg'].map(function(size){
23102 if (settings[size]) {
23103 cfg.cls += ' col-' + size + '-' + settings[size];
23107 var inputblock = input;
23109 if (this.before || this.after) {
23112 cls : 'input-group',
23117 inputblock.cn.push({
23119 cls : 'input-group-addon',
23124 inputblock.cn.push(input);
23126 if(this.inputType != 'radio'){
23127 inputblock.cn.push(hidden);
23131 inputblock.cn.push({
23133 cls : 'input-group-addon',
23139 var boxLabelCfg = false;
23145 //'for': id, // box label is handled by onclick - so no for...
23147 html: this.boxLabel
23150 boxLabelCfg.tooltip = this.tooltip;
23156 if (align ==='left' && this.fieldLabel.length) {
23157 // Roo.log("left and has label");
23162 cls : 'control-label',
23163 html : this.fieldLabel
23174 cfg.cn[1].cn.push(boxLabelCfg);
23177 if(this.labelWidth > 12){
23178 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23181 if(this.labelWidth < 13 && this.labelmd == 0){
23182 this.labelmd = this.labelWidth;
23185 if(this.labellg > 0){
23186 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23187 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23190 if(this.labelmd > 0){
23191 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23192 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23195 if(this.labelsm > 0){
23196 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23197 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23200 if(this.labelxs > 0){
23201 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23202 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23205 } else if ( this.fieldLabel.length) {
23206 // Roo.log(" label");
23210 tag: this.boxLabel ? 'span' : 'label',
23212 cls: 'control-label box-input-label',
23213 //cls : 'input-group-addon',
23214 html : this.fieldLabel
23221 cfg.cn.push(boxLabelCfg);
23226 // Roo.log(" no label && no align");
23227 cfg.cn = [ inputblock ] ;
23229 cfg.cn.push(boxLabelCfg);
23237 if(this.inputType != 'radio'){
23238 cfg.cn.push(hidden);
23246 * return the real input element.
23248 inputEl: function ()
23250 return this.el.select('input.roo-' + this.inputType,true).first();
23252 hiddenEl: function ()
23254 return this.el.select('input.roo-hidden-value',true).first();
23257 labelEl: function()
23259 return this.el.select('label.control-label',true).first();
23261 /* depricated... */
23265 return this.labelEl();
23268 boxLabelEl: function()
23270 return this.el.select('label.box-label',true).first();
23273 initEvents : function()
23275 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23277 this.inputEl().on('click', this.onClick, this);
23279 if (this.boxLabel) {
23280 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23283 this.startValue = this.getValue();
23286 Roo.bootstrap.CheckBox.register(this);
23290 onClick : function(e)
23292 if(this.fireEvent('click', this, e) !== false){
23293 this.setChecked(!this.checked);
23298 setChecked : function(state,suppressEvent)
23300 this.startValue = this.getValue();
23302 if(this.inputType == 'radio'){
23304 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23305 e.dom.checked = false;
23308 this.inputEl().dom.checked = true;
23310 this.inputEl().dom.value = this.inputValue;
23312 if(suppressEvent !== true){
23313 this.fireEvent('check', this, true);
23321 this.checked = state;
23323 this.inputEl().dom.checked = state;
23326 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23328 if(suppressEvent !== true){
23329 this.fireEvent('check', this, state);
23335 getValue : function()
23337 if(this.inputType == 'radio'){
23338 return this.getGroupValue();
23341 return this.hiddenEl().dom.value;
23345 getGroupValue : function()
23347 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23351 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23354 setValue : function(v,suppressEvent)
23356 if(this.inputType == 'radio'){
23357 this.setGroupValue(v, suppressEvent);
23361 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23366 setGroupValue : function(v, suppressEvent)
23368 this.startValue = this.getValue();
23370 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23371 e.dom.checked = false;
23373 if(e.dom.value == v){
23374 e.dom.checked = true;
23378 if(suppressEvent !== true){
23379 this.fireEvent('check', this, true);
23387 validate : function()
23389 if(this.getVisibilityEl().hasClass('hidden')){
23395 (this.inputType == 'radio' && this.validateRadio()) ||
23396 (this.inputType == 'checkbox' && this.validateCheckbox())
23402 this.markInvalid();
23406 validateRadio : function()
23408 if(this.getVisibilityEl().hasClass('hidden')){
23412 if(this.allowBlank){
23418 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23419 if(!e.dom.checked){
23431 validateCheckbox : function()
23434 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23435 //return (this.getValue() == this.inputValue) ? true : false;
23438 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23446 for(var i in group){
23447 if(group[i].el.isVisible(true)){
23455 for(var i in group){
23460 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23467 * Mark this field as valid
23469 markValid : function()
23473 this.fireEvent('valid', this);
23475 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23478 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23485 if(this.inputType == 'radio'){
23486 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23487 var fg = e.findParent('.form-group', false, true);
23488 if (Roo.bootstrap.version == 3) {
23489 fg.removeClass([_this.invalidClass, _this.validClass]);
23490 fg.addClass(_this.validClass);
23492 fg.removeClass(['is-valid', 'is-invalid']);
23493 fg.addClass('is-valid');
23501 var fg = this.el.findParent('.form-group', false, true);
23502 if (Roo.bootstrap.version == 3) {
23503 fg.removeClass([this.invalidClass, this.validClass]);
23504 fg.addClass(this.validClass);
23506 fg.removeClass(['is-valid', 'is-invalid']);
23507 fg.addClass('is-valid');
23512 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23518 for(var i in group){
23519 var fg = group[i].el.findParent('.form-group', false, true);
23520 if (Roo.bootstrap.version == 3) {
23521 fg.removeClass([this.invalidClass, this.validClass]);
23522 fg.addClass(this.validClass);
23524 fg.removeClass(['is-valid', 'is-invalid']);
23525 fg.addClass('is-valid');
23531 * Mark this field as invalid
23532 * @param {String} msg The validation message
23534 markInvalid : function(msg)
23536 if(this.allowBlank){
23542 this.fireEvent('invalid', this, msg);
23544 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23547 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23551 label.markInvalid();
23554 if(this.inputType == 'radio'){
23556 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23557 var fg = e.findParent('.form-group', false, true);
23558 if (Roo.bootstrap.version == 3) {
23559 fg.removeClass([_this.invalidClass, _this.validClass]);
23560 fg.addClass(_this.invalidClass);
23562 fg.removeClass(['is-invalid', 'is-valid']);
23563 fg.addClass('is-invalid');
23571 var fg = this.el.findParent('.form-group', false, true);
23572 if (Roo.bootstrap.version == 3) {
23573 fg.removeClass([_this.invalidClass, _this.validClass]);
23574 fg.addClass(_this.invalidClass);
23576 fg.removeClass(['is-invalid', 'is-valid']);
23577 fg.addClass('is-invalid');
23582 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23588 for(var i in group){
23589 var fg = group[i].el.findParent('.form-group', false, true);
23590 if (Roo.bootstrap.version == 3) {
23591 fg.removeClass([_this.invalidClass, _this.validClass]);
23592 fg.addClass(_this.invalidClass);
23594 fg.removeClass(['is-invalid', 'is-valid']);
23595 fg.addClass('is-invalid');
23601 clearInvalid : function()
23603 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23605 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23607 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23609 if (label && label.iconEl) {
23610 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23611 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23615 disable : function()
23617 if(this.inputType != 'radio'){
23618 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23625 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23626 _this.getActionEl().addClass(this.disabledClass);
23627 e.dom.disabled = true;
23631 this.disabled = true;
23632 this.fireEvent("disable", this);
23636 enable : function()
23638 if(this.inputType != 'radio'){
23639 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23646 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23647 _this.getActionEl().removeClass(this.disabledClass);
23648 e.dom.disabled = false;
23652 this.disabled = false;
23653 this.fireEvent("enable", this);
23657 setBoxLabel : function(v)
23662 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23668 Roo.apply(Roo.bootstrap.CheckBox, {
23673 * register a CheckBox Group
23674 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23676 register : function(checkbox)
23678 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23679 this.groups[checkbox.groupId] = {};
23682 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23686 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23690 * fetch a CheckBox Group based on the group ID
23691 * @param {string} the group ID
23692 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23694 get: function(groupId) {
23695 if (typeof(this.groups[groupId]) == 'undefined') {
23699 return this.groups[groupId] ;
23712 * @class Roo.bootstrap.Radio
23713 * @extends Roo.bootstrap.Component
23714 * Bootstrap Radio class
23715 * @cfg {String} boxLabel - the label associated
23716 * @cfg {String} value - the value of radio
23719 * Create a new Radio
23720 * @param {Object} config The config object
23722 Roo.bootstrap.Radio = function(config){
23723 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23727 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23733 getAutoCreate : function()
23737 cls : 'form-group radio',
23742 html : this.boxLabel
23750 initEvents : function()
23752 this.parent().register(this);
23754 this.el.on('click', this.onClick, this);
23758 onClick : function(e)
23760 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23761 this.setChecked(true);
23765 setChecked : function(state, suppressEvent)
23767 this.parent().setValue(this.value, suppressEvent);
23771 setBoxLabel : function(v)
23776 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23791 * @class Roo.bootstrap.SecurePass
23792 * @extends Roo.bootstrap.Input
23793 * Bootstrap SecurePass class
23797 * Create a new SecurePass
23798 * @param {Object} config The config object
23801 Roo.bootstrap.SecurePass = function (config) {
23802 // these go here, so the translation tool can replace them..
23804 PwdEmpty: "Please type a password, and then retype it to confirm.",
23805 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23806 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23807 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23808 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23809 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23810 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23811 TooWeak: "Your password is Too Weak."
23813 this.meterLabel = "Password strength:";
23814 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23815 this.meterClass = [
23816 "roo-password-meter-tooweak",
23817 "roo-password-meter-weak",
23818 "roo-password-meter-medium",
23819 "roo-password-meter-strong",
23820 "roo-password-meter-grey"
23825 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23828 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23830 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23832 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23833 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23834 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23835 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23836 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23837 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23838 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23848 * @cfg {String/Object} Label for the strength meter (defaults to
23849 * 'Password strength:')
23854 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23855 * ['Weak', 'Medium', 'Strong'])
23858 pwdStrengths: false,
23871 initEvents: function ()
23873 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23875 if (this.el.is('input[type=password]') && Roo.isSafari) {
23876 this.el.on('keydown', this.SafariOnKeyDown, this);
23879 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23882 onRender: function (ct, position)
23884 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23885 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23886 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23888 this.trigger.createChild({
23893 cls: 'roo-password-meter-grey col-xs-12',
23896 //width: this.meterWidth + 'px'
23900 cls: 'roo-password-meter-text'
23906 if (this.hideTrigger) {
23907 this.trigger.setDisplayed(false);
23909 this.setSize(this.width || '', this.height || '');
23912 onDestroy: function ()
23914 if (this.trigger) {
23915 this.trigger.removeAllListeners();
23916 this.trigger.remove();
23919 this.wrap.remove();
23921 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23924 checkStrength: function ()
23926 var pwd = this.inputEl().getValue();
23927 if (pwd == this._lastPwd) {
23932 if (this.ClientSideStrongPassword(pwd)) {
23934 } else if (this.ClientSideMediumPassword(pwd)) {
23936 } else if (this.ClientSideWeakPassword(pwd)) {
23942 Roo.log('strength1: ' + strength);
23944 //var pm = this.trigger.child('div/div/div').dom;
23945 var pm = this.trigger.child('div/div');
23946 pm.removeClass(this.meterClass);
23947 pm.addClass(this.meterClass[strength]);
23950 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23952 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23954 this._lastPwd = pwd;
23958 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23960 this._lastPwd = '';
23962 var pm = this.trigger.child('div/div');
23963 pm.removeClass(this.meterClass);
23964 pm.addClass('roo-password-meter-grey');
23967 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23970 this.inputEl().dom.type='password';
23973 validateValue: function (value)
23975 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23978 if (value.length == 0) {
23979 if (this.allowBlank) {
23980 this.clearInvalid();
23984 this.markInvalid(this.errors.PwdEmpty);
23985 this.errorMsg = this.errors.PwdEmpty;
23993 if (!value.match(/[\x21-\x7e]+/)) {
23994 this.markInvalid(this.errors.PwdBadChar);
23995 this.errorMsg = this.errors.PwdBadChar;
23998 if (value.length < 6) {
23999 this.markInvalid(this.errors.PwdShort);
24000 this.errorMsg = this.errors.PwdShort;
24003 if (value.length > 16) {
24004 this.markInvalid(this.errors.PwdLong);
24005 this.errorMsg = this.errors.PwdLong;
24009 if (this.ClientSideStrongPassword(value)) {
24011 } else if (this.ClientSideMediumPassword(value)) {
24013 } else if (this.ClientSideWeakPassword(value)) {
24020 if (strength < 2) {
24021 //this.markInvalid(this.errors.TooWeak);
24022 this.errorMsg = this.errors.TooWeak;
24027 console.log('strength2: ' + strength);
24029 //var pm = this.trigger.child('div/div/div').dom;
24031 var pm = this.trigger.child('div/div');
24032 pm.removeClass(this.meterClass);
24033 pm.addClass(this.meterClass[strength]);
24035 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24037 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24039 this.errorMsg = '';
24043 CharacterSetChecks: function (type)
24046 this.fResult = false;
24049 isctype: function (character, type)
24052 case this.kCapitalLetter:
24053 if (character >= 'A' && character <= 'Z') {
24058 case this.kSmallLetter:
24059 if (character >= 'a' && character <= 'z') {
24065 if (character >= '0' && character <= '9') {
24070 case this.kPunctuation:
24071 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24082 IsLongEnough: function (pwd, size)
24084 return !(pwd == null || isNaN(size) || pwd.length < size);
24087 SpansEnoughCharacterSets: function (word, nb)
24089 if (!this.IsLongEnough(word, nb))
24094 var characterSetChecks = new Array(
24095 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24096 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24099 for (var index = 0; index < word.length; ++index) {
24100 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24101 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24102 characterSetChecks[nCharSet].fResult = true;
24109 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24110 if (characterSetChecks[nCharSet].fResult) {
24115 if (nCharSets < nb) {
24121 ClientSideStrongPassword: function (pwd)
24123 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24126 ClientSideMediumPassword: function (pwd)
24128 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24131 ClientSideWeakPassword: function (pwd)
24133 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24136 })//<script type="text/javascript">
24139 * Based Ext JS Library 1.1.1
24140 * Copyright(c) 2006-2007, Ext JS, LLC.
24146 * @class Roo.HtmlEditorCore
24147 * @extends Roo.Component
24148 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24150 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24153 Roo.HtmlEditorCore = function(config){
24156 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24161 * @event initialize
24162 * Fires when the editor is fully initialized (including the iframe)
24163 * @param {Roo.HtmlEditorCore} this
24168 * Fires when the editor is first receives the focus. Any insertion must wait
24169 * until after this event.
24170 * @param {Roo.HtmlEditorCore} this
24174 * @event beforesync
24175 * Fires before the textarea is updated with content from the editor iframe. Return false
24176 * to cancel the sync.
24177 * @param {Roo.HtmlEditorCore} this
24178 * @param {String} html
24182 * @event beforepush
24183 * Fires before the iframe editor is updated with content from the textarea. Return false
24184 * to cancel the push.
24185 * @param {Roo.HtmlEditorCore} this
24186 * @param {String} html
24191 * Fires when the textarea is updated with content from the editor iframe.
24192 * @param {Roo.HtmlEditorCore} this
24193 * @param {String} html
24198 * Fires when the iframe editor is updated with content from the textarea.
24199 * @param {Roo.HtmlEditorCore} this
24200 * @param {String} html
24205 * @event editorevent
24206 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24207 * @param {Roo.HtmlEditorCore} this
24213 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24215 // defaults : white / black...
24216 this.applyBlacklists();
24223 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24227 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24233 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24238 * @cfg {Number} height (in pixels)
24242 * @cfg {Number} width (in pixels)
24247 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24250 stylesheets: false,
24255 // private properties
24256 validationEvent : false,
24258 initialized : false,
24260 sourceEditMode : false,
24261 onFocus : Roo.emptyFn,
24263 hideMode:'offsets',
24267 // blacklist + whitelisted elements..
24274 * Protected method that will not generally be called directly. It
24275 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24276 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24278 getDocMarkup : function(){
24282 // inherit styels from page...??
24283 if (this.stylesheets === false) {
24285 Roo.get(document.head).select('style').each(function(node) {
24286 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24289 Roo.get(document.head).select('link').each(function(node) {
24290 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24293 } else if (!this.stylesheets.length) {
24295 st = '<style type="text/css">' +
24296 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24299 for (var i in this.stylesheets) {
24300 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24305 st += '<style type="text/css">' +
24306 'IMG { cursor: pointer } ' +
24309 var cls = 'roo-htmleditor-body';
24311 if(this.bodyCls.length){
24312 cls += ' ' + this.bodyCls;
24315 return '<html><head>' + st +
24316 //<style type="text/css">' +
24317 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24319 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24323 onRender : function(ct, position)
24326 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24327 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24330 this.el.dom.style.border = '0 none';
24331 this.el.dom.setAttribute('tabIndex', -1);
24332 this.el.addClass('x-hidden hide');
24336 if(Roo.isIE){ // fix IE 1px bogus margin
24337 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24341 this.frameId = Roo.id();
24345 var iframe = this.owner.wrap.createChild({
24347 cls: 'form-control', // bootstrap..
24349 name: this.frameId,
24350 frameBorder : 'no',
24351 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24356 this.iframe = iframe.dom;
24358 this.assignDocWin();
24360 this.doc.designMode = 'on';
24363 this.doc.write(this.getDocMarkup());
24367 var task = { // must defer to wait for browser to be ready
24369 //console.log("run task?" + this.doc.readyState);
24370 this.assignDocWin();
24371 if(this.doc.body || this.doc.readyState == 'complete'){
24373 this.doc.designMode="on";
24377 Roo.TaskMgr.stop(task);
24378 this.initEditor.defer(10, this);
24385 Roo.TaskMgr.start(task);
24390 onResize : function(w, h)
24392 Roo.log('resize: ' +w + ',' + h );
24393 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24397 if(typeof w == 'number'){
24399 this.iframe.style.width = w + 'px';
24401 if(typeof h == 'number'){
24403 this.iframe.style.height = h + 'px';
24405 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24412 * Toggles the editor between standard and source edit mode.
24413 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24415 toggleSourceEdit : function(sourceEditMode){
24417 this.sourceEditMode = sourceEditMode === true;
24419 if(this.sourceEditMode){
24421 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24424 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24425 //this.iframe.className = '';
24428 //this.setSize(this.owner.wrap.getSize());
24429 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24436 * Protected method that will not generally be called directly. If you need/want
24437 * custom HTML cleanup, this is the method you should override.
24438 * @param {String} html The HTML to be cleaned
24439 * return {String} The cleaned HTML
24441 cleanHtml : function(html){
24442 html = String(html);
24443 if(html.length > 5){
24444 if(Roo.isSafari){ // strip safari nonsense
24445 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24448 if(html == ' '){
24455 * HTML Editor -> Textarea
24456 * Protected method that will not generally be called directly. Syncs the contents
24457 * of the editor iframe with the textarea.
24459 syncValue : function(){
24460 if(this.initialized){
24461 var bd = (this.doc.body || this.doc.documentElement);
24462 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24463 var html = bd.innerHTML;
24465 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24466 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24468 html = '<div style="'+m[0]+'">' + html + '</div>';
24471 html = this.cleanHtml(html);
24472 // fix up the special chars.. normaly like back quotes in word...
24473 // however we do not want to do this with chinese..
24474 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24476 var cc = match.charCodeAt();
24478 // Get the character value, handling surrogate pairs
24479 if (match.length == 2) {
24480 // It's a surrogate pair, calculate the Unicode code point
24481 var high = match.charCodeAt(0) - 0xD800;
24482 var low = match.charCodeAt(1) - 0xDC00;
24483 cc = (high * 0x400) + low + 0x10000;
24485 (cc >= 0x4E00 && cc < 0xA000 ) ||
24486 (cc >= 0x3400 && cc < 0x4E00 ) ||
24487 (cc >= 0xf900 && cc < 0xfb00 )
24492 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24493 return "&#" + cc + ";";
24500 if(this.owner.fireEvent('beforesync', this, html) !== false){
24501 this.el.dom.value = html;
24502 this.owner.fireEvent('sync', this, html);
24508 * Protected method that will not generally be called directly. Pushes the value of the textarea
24509 * into the iframe editor.
24511 pushValue : function(){
24512 if(this.initialized){
24513 var v = this.el.dom.value.trim();
24515 // if(v.length < 1){
24519 if(this.owner.fireEvent('beforepush', this, v) !== false){
24520 var d = (this.doc.body || this.doc.documentElement);
24522 this.cleanUpPaste();
24523 this.el.dom.value = d.innerHTML;
24524 this.owner.fireEvent('push', this, v);
24530 deferFocus : function(){
24531 this.focus.defer(10, this);
24535 focus : function(){
24536 if(this.win && !this.sourceEditMode){
24543 assignDocWin: function()
24545 var iframe = this.iframe;
24548 this.doc = iframe.contentWindow.document;
24549 this.win = iframe.contentWindow;
24551 // if (!Roo.get(this.frameId)) {
24554 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24555 // this.win = Roo.get(this.frameId).dom.contentWindow;
24557 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24561 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24562 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24567 initEditor : function(){
24568 //console.log("INIT EDITOR");
24569 this.assignDocWin();
24573 this.doc.designMode="on";
24575 this.doc.write(this.getDocMarkup());
24578 var dbody = (this.doc.body || this.doc.documentElement);
24579 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24580 // this copies styles from the containing element into thsi one..
24581 // not sure why we need all of this..
24582 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24584 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24585 //ss['background-attachment'] = 'fixed'; // w3c
24586 dbody.bgProperties = 'fixed'; // ie
24587 //Roo.DomHelper.applyStyles(dbody, ss);
24588 Roo.EventManager.on(this.doc, {
24589 //'mousedown': this.onEditorEvent,
24590 'mouseup': this.onEditorEvent,
24591 'dblclick': this.onEditorEvent,
24592 'click': this.onEditorEvent,
24593 'keyup': this.onEditorEvent,
24598 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24600 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24601 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24603 this.initialized = true;
24605 this.owner.fireEvent('initialize', this);
24610 onDestroy : function(){
24616 //for (var i =0; i < this.toolbars.length;i++) {
24617 // // fixme - ask toolbars for heights?
24618 // this.toolbars[i].onDestroy();
24621 //this.wrap.dom.innerHTML = '';
24622 //this.wrap.remove();
24627 onFirstFocus : function(){
24629 this.assignDocWin();
24632 this.activated = true;
24635 if(Roo.isGecko){ // prevent silly gecko errors
24637 var s = this.win.getSelection();
24638 if(!s.focusNode || s.focusNode.nodeType != 3){
24639 var r = s.getRangeAt(0);
24640 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24645 this.execCmd('useCSS', true);
24646 this.execCmd('styleWithCSS', false);
24649 this.owner.fireEvent('activate', this);
24653 adjustFont: function(btn){
24654 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24655 //if(Roo.isSafari){ // safari
24658 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24659 if(Roo.isSafari){ // safari
24660 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24661 v = (v < 10) ? 10 : v;
24662 v = (v > 48) ? 48 : v;
24663 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24668 v = Math.max(1, v+adjust);
24670 this.execCmd('FontSize', v );
24673 onEditorEvent : function(e)
24675 this.owner.fireEvent('editorevent', this, e);
24676 // this.updateToolbar();
24677 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24680 insertTag : function(tg)
24682 // could be a bit smarter... -> wrap the current selected tRoo..
24683 if (tg.toLowerCase() == 'span' ||
24684 tg.toLowerCase() == 'code' ||
24685 tg.toLowerCase() == 'sup' ||
24686 tg.toLowerCase() == 'sub'
24689 range = this.createRange(this.getSelection());
24690 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24691 wrappingNode.appendChild(range.extractContents());
24692 range.insertNode(wrappingNode);
24699 this.execCmd("formatblock", tg);
24703 insertText : function(txt)
24707 var range = this.createRange();
24708 range.deleteContents();
24709 //alert(Sender.getAttribute('label'));
24711 range.insertNode(this.doc.createTextNode(txt));
24717 * Executes a Midas editor command on the editor document and performs necessary focus and
24718 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24719 * @param {String} cmd The Midas command
24720 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24722 relayCmd : function(cmd, value){
24724 this.execCmd(cmd, value);
24725 this.owner.fireEvent('editorevent', this);
24726 //this.updateToolbar();
24727 this.owner.deferFocus();
24731 * Executes a Midas editor command directly on the editor document.
24732 * For visual commands, you should use {@link #relayCmd} instead.
24733 * <b>This should only be called after the editor is initialized.</b>
24734 * @param {String} cmd The Midas command
24735 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24737 execCmd : function(cmd, value){
24738 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24745 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24747 * @param {String} text | dom node..
24749 insertAtCursor : function(text)
24752 if(!this.activated){
24758 var r = this.doc.selection.createRange();
24769 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24773 // from jquery ui (MIT licenced)
24775 var win = this.win;
24777 if (win.getSelection && win.getSelection().getRangeAt) {
24778 range = win.getSelection().getRangeAt(0);
24779 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24780 range.insertNode(node);
24781 } else if (win.document.selection && win.document.selection.createRange) {
24782 // no firefox support
24783 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24784 win.document.selection.createRange().pasteHTML(txt);
24786 // no firefox support
24787 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24788 this.execCmd('InsertHTML', txt);
24797 mozKeyPress : function(e){
24799 var c = e.getCharCode(), cmd;
24802 c = String.fromCharCode(c).toLowerCase();
24816 this.cleanUpPaste.defer(100, this);
24824 e.preventDefault();
24832 fixKeys : function(){ // load time branching for fastest keydown performance
24834 return function(e){
24835 var k = e.getKey(), r;
24838 r = this.doc.selection.createRange();
24841 r.pasteHTML('    ');
24848 r = this.doc.selection.createRange();
24850 var target = r.parentElement();
24851 if(!target || target.tagName.toLowerCase() != 'li'){
24853 r.pasteHTML('<br />');
24859 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24860 this.cleanUpPaste.defer(100, this);
24866 }else if(Roo.isOpera){
24867 return function(e){
24868 var k = e.getKey();
24872 this.execCmd('InsertHTML','    ');
24875 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24876 this.cleanUpPaste.defer(100, this);
24881 }else if(Roo.isSafari){
24882 return function(e){
24883 var k = e.getKey();
24887 this.execCmd('InsertText','\t');
24891 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24892 this.cleanUpPaste.defer(100, this);
24900 getAllAncestors: function()
24902 var p = this.getSelectedNode();
24905 a.push(p); // push blank onto stack..
24906 p = this.getParentElement();
24910 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24914 a.push(this.doc.body);
24918 lastSelNode : false,
24921 getSelection : function()
24923 this.assignDocWin();
24924 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24927 getSelectedNode: function()
24929 // this may only work on Gecko!!!
24931 // should we cache this!!!!
24936 var range = this.createRange(this.getSelection()).cloneRange();
24939 var parent = range.parentElement();
24941 var testRange = range.duplicate();
24942 testRange.moveToElementText(parent);
24943 if (testRange.inRange(range)) {
24946 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24949 parent = parent.parentElement;
24954 // is ancestor a text element.
24955 var ac = range.commonAncestorContainer;
24956 if (ac.nodeType == 3) {
24957 ac = ac.parentNode;
24960 var ar = ac.childNodes;
24963 var other_nodes = [];
24964 var has_other_nodes = false;
24965 for (var i=0;i<ar.length;i++) {
24966 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24969 // fullly contained node.
24971 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24976 // probably selected..
24977 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24978 other_nodes.push(ar[i]);
24982 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24987 has_other_nodes = true;
24989 if (!nodes.length && other_nodes.length) {
24990 nodes= other_nodes;
24992 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24998 createRange: function(sel)
25000 // this has strange effects when using with
25001 // top toolbar - not sure if it's a great idea.
25002 //this.editor.contentWindow.focus();
25003 if (typeof sel != "undefined") {
25005 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25007 return this.doc.createRange();
25010 return this.doc.createRange();
25013 getParentElement: function()
25016 this.assignDocWin();
25017 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25019 var range = this.createRange(sel);
25022 var p = range.commonAncestorContainer;
25023 while (p.nodeType == 3) { // text node
25034 * Range intersection.. the hard stuff...
25038 * [ -- selected range --- ]
25042 * if end is before start or hits it. fail.
25043 * if start is after end or hits it fail.
25045 * if either hits (but other is outside. - then it's not
25051 // @see http://www.thismuchiknow.co.uk/?p=64.
25052 rangeIntersectsNode : function(range, node)
25054 var nodeRange = node.ownerDocument.createRange();
25056 nodeRange.selectNode(node);
25058 nodeRange.selectNodeContents(node);
25061 var rangeStartRange = range.cloneRange();
25062 rangeStartRange.collapse(true);
25064 var rangeEndRange = range.cloneRange();
25065 rangeEndRange.collapse(false);
25067 var nodeStartRange = nodeRange.cloneRange();
25068 nodeStartRange.collapse(true);
25070 var nodeEndRange = nodeRange.cloneRange();
25071 nodeEndRange.collapse(false);
25073 return rangeStartRange.compareBoundaryPoints(
25074 Range.START_TO_START, nodeEndRange) == -1 &&
25075 rangeEndRange.compareBoundaryPoints(
25076 Range.START_TO_START, nodeStartRange) == 1;
25080 rangeCompareNode : function(range, node)
25082 var nodeRange = node.ownerDocument.createRange();
25084 nodeRange.selectNode(node);
25086 nodeRange.selectNodeContents(node);
25090 range.collapse(true);
25092 nodeRange.collapse(true);
25094 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25095 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25097 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25099 var nodeIsBefore = ss == 1;
25100 var nodeIsAfter = ee == -1;
25102 if (nodeIsBefore && nodeIsAfter) {
25105 if (!nodeIsBefore && nodeIsAfter) {
25106 return 1; //right trailed.
25109 if (nodeIsBefore && !nodeIsAfter) {
25110 return 2; // left trailed.
25116 // private? - in a new class?
25117 cleanUpPaste : function()
25119 // cleans up the whole document..
25120 Roo.log('cleanuppaste');
25122 this.cleanUpChildren(this.doc.body);
25123 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25124 if (clean != this.doc.body.innerHTML) {
25125 this.doc.body.innerHTML = clean;
25130 cleanWordChars : function(input) {// change the chars to hex code
25131 var he = Roo.HtmlEditorCore;
25133 var output = input;
25134 Roo.each(he.swapCodes, function(sw) {
25135 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25137 output = output.replace(swapper, sw[1]);
25144 cleanUpChildren : function (n)
25146 if (!n.childNodes.length) {
25149 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25150 this.cleanUpChild(n.childNodes[i]);
25157 cleanUpChild : function (node)
25160 //console.log(node);
25161 if (node.nodeName == "#text") {
25162 // clean up silly Windows -- stuff?
25165 if (node.nodeName == "#comment") {
25166 node.parentNode.removeChild(node);
25167 // clean up silly Windows -- stuff?
25170 var lcname = node.tagName.toLowerCase();
25171 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25172 // whitelist of tags..
25174 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25176 node.parentNode.removeChild(node);
25181 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25183 // spans with no attributes - just remove them..
25184 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25185 remove_keep_children = true;
25188 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25189 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25191 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25192 // remove_keep_children = true;
25195 if (remove_keep_children) {
25196 this.cleanUpChildren(node);
25197 // inserts everything just before this node...
25198 while (node.childNodes.length) {
25199 var cn = node.childNodes[0];
25200 node.removeChild(cn);
25201 node.parentNode.insertBefore(cn, node);
25203 node.parentNode.removeChild(node);
25207 if (!node.attributes || !node.attributes.length) {
25212 this.cleanUpChildren(node);
25216 function cleanAttr(n,v)
25219 if (v.match(/^\./) || v.match(/^\//)) {
25222 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25225 if (v.match(/^#/)) {
25228 if (v.match(/^\{/)) { // allow template editing.
25231 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25232 node.removeAttribute(n);
25236 var cwhite = this.cwhite;
25237 var cblack = this.cblack;
25239 function cleanStyle(n,v)
25241 if (v.match(/expression/)) { //XSS?? should we even bother..
25242 node.removeAttribute(n);
25246 var parts = v.split(/;/);
25249 Roo.each(parts, function(p) {
25250 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25254 var l = p.split(':').shift().replace(/\s+/g,'');
25255 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25257 if ( cwhite.length && cblack.indexOf(l) > -1) {
25258 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25259 //node.removeAttribute(n);
25263 // only allow 'c whitelisted system attributes'
25264 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25265 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25266 //node.removeAttribute(n);
25276 if (clean.length) {
25277 node.setAttribute(n, clean.join(';'));
25279 node.removeAttribute(n);
25285 for (var i = node.attributes.length-1; i > -1 ; i--) {
25286 var a = node.attributes[i];
25289 if (a.name.toLowerCase().substr(0,2)=='on') {
25290 node.removeAttribute(a.name);
25293 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25294 node.removeAttribute(a.name);
25297 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25298 cleanAttr(a.name,a.value); // fixme..
25301 if (a.name == 'style') {
25302 cleanStyle(a.name,a.value);
25305 /// clean up MS crap..
25306 // tecnically this should be a list of valid class'es..
25309 if (a.name == 'class') {
25310 if (a.value.match(/^Mso/)) {
25311 node.removeAttribute('class');
25314 if (a.value.match(/^body$/)) {
25315 node.removeAttribute('class');
25326 this.cleanUpChildren(node);
25332 * Clean up MS wordisms...
25334 cleanWord : function(node)
25337 this.cleanWord(this.doc.body);
25342 node.nodeName == 'SPAN' &&
25343 !node.hasAttributes() &&
25344 node.childNodes.length == 1 &&
25345 node.firstChild.nodeName == "#text"
25347 var textNode = node.firstChild;
25348 node.removeChild(textNode);
25349 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25350 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25352 node.parentNode.insertBefore(textNode, node);
25353 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25354 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25356 node.parentNode.removeChild(node);
25359 if (node.nodeName == "#text") {
25360 // clean up silly Windows -- stuff?
25363 if (node.nodeName == "#comment") {
25364 node.parentNode.removeChild(node);
25365 // clean up silly Windows -- stuff?
25369 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25370 node.parentNode.removeChild(node);
25373 //Roo.log(node.tagName);
25374 // remove - but keep children..
25375 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25376 //Roo.log('-- removed');
25377 while (node.childNodes.length) {
25378 var cn = node.childNodes[0];
25379 node.removeChild(cn);
25380 node.parentNode.insertBefore(cn, node);
25381 // move node to parent - and clean it..
25382 this.cleanWord(cn);
25384 node.parentNode.removeChild(node);
25385 /// no need to iterate chidlren = it's got none..
25386 //this.iterateChildren(node, this.cleanWord);
25390 if (node.className.length) {
25392 var cn = node.className.split(/\W+/);
25394 Roo.each(cn, function(cls) {
25395 if (cls.match(/Mso[a-zA-Z]+/)) {
25400 node.className = cna.length ? cna.join(' ') : '';
25402 node.removeAttribute("class");
25406 if (node.hasAttribute("lang")) {
25407 node.removeAttribute("lang");
25410 if (node.hasAttribute("style")) {
25412 var styles = node.getAttribute("style").split(";");
25414 Roo.each(styles, function(s) {
25415 if (!s.match(/:/)) {
25418 var kv = s.split(":");
25419 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25422 // what ever is left... we allow.
25425 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25426 if (!nstyle.length) {
25427 node.removeAttribute('style');
25430 this.iterateChildren(node, this.cleanWord);
25436 * iterateChildren of a Node, calling fn each time, using this as the scole..
25437 * @param {DomNode} node node to iterate children of.
25438 * @param {Function} fn method of this class to call on each item.
25440 iterateChildren : function(node, fn)
25442 if (!node.childNodes.length) {
25445 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25446 fn.call(this, node.childNodes[i])
25452 * cleanTableWidths.
25454 * Quite often pasting from word etc.. results in tables with column and widths.
25455 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25458 cleanTableWidths : function(node)
25463 this.cleanTableWidths(this.doc.body);
25468 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25471 Roo.log(node.tagName);
25472 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25473 this.iterateChildren(node, this.cleanTableWidths);
25476 if (node.hasAttribute('width')) {
25477 node.removeAttribute('width');
25481 if (node.hasAttribute("style")) {
25484 var styles = node.getAttribute("style").split(";");
25486 Roo.each(styles, function(s) {
25487 if (!s.match(/:/)) {
25490 var kv = s.split(":");
25491 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25494 // what ever is left... we allow.
25497 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25498 if (!nstyle.length) {
25499 node.removeAttribute('style');
25503 this.iterateChildren(node, this.cleanTableWidths);
25511 domToHTML : function(currentElement, depth, nopadtext) {
25513 depth = depth || 0;
25514 nopadtext = nopadtext || false;
25516 if (!currentElement) {
25517 return this.domToHTML(this.doc.body);
25520 //Roo.log(currentElement);
25522 var allText = false;
25523 var nodeName = currentElement.nodeName;
25524 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25526 if (nodeName == '#text') {
25528 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25533 if (nodeName != 'BODY') {
25536 // Prints the node tagName, such as <A>, <IMG>, etc
25539 for(i = 0; i < currentElement.attributes.length;i++) {
25541 var aname = currentElement.attributes.item(i).name;
25542 if (!currentElement.attributes.item(i).value.length) {
25545 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25548 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25557 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25560 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25565 // Traverse the tree
25567 var currentElementChild = currentElement.childNodes.item(i);
25568 var allText = true;
25569 var innerHTML = '';
25571 while (currentElementChild) {
25572 // Formatting code (indent the tree so it looks nice on the screen)
25573 var nopad = nopadtext;
25574 if (lastnode == 'SPAN') {
25578 if (currentElementChild.nodeName == '#text') {
25579 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25580 toadd = nopadtext ? toadd : toadd.trim();
25581 if (!nopad && toadd.length > 80) {
25582 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25584 innerHTML += toadd;
25587 currentElementChild = currentElement.childNodes.item(i);
25593 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25595 // Recursively traverse the tree structure of the child node
25596 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25597 lastnode = currentElementChild.nodeName;
25599 currentElementChild=currentElement.childNodes.item(i);
25605 // The remaining code is mostly for formatting the tree
25606 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25611 ret+= "</"+tagName+">";
25617 applyBlacklists : function()
25619 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25620 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25624 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25625 if (b.indexOf(tag) > -1) {
25628 this.white.push(tag);
25632 Roo.each(w, function(tag) {
25633 if (b.indexOf(tag) > -1) {
25636 if (this.white.indexOf(tag) > -1) {
25639 this.white.push(tag);
25644 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25645 if (w.indexOf(tag) > -1) {
25648 this.black.push(tag);
25652 Roo.each(b, function(tag) {
25653 if (w.indexOf(tag) > -1) {
25656 if (this.black.indexOf(tag) > -1) {
25659 this.black.push(tag);
25664 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25665 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25669 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25670 if (b.indexOf(tag) > -1) {
25673 this.cwhite.push(tag);
25677 Roo.each(w, function(tag) {
25678 if (b.indexOf(tag) > -1) {
25681 if (this.cwhite.indexOf(tag) > -1) {
25684 this.cwhite.push(tag);
25689 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25690 if (w.indexOf(tag) > -1) {
25693 this.cblack.push(tag);
25697 Roo.each(b, function(tag) {
25698 if (w.indexOf(tag) > -1) {
25701 if (this.cblack.indexOf(tag) > -1) {
25704 this.cblack.push(tag);
25709 setStylesheets : function(stylesheets)
25711 if(typeof(stylesheets) == 'string'){
25712 Roo.get(this.iframe.contentDocument.head).createChild({
25714 rel : 'stylesheet',
25723 Roo.each(stylesheets, function(s) {
25728 Roo.get(_this.iframe.contentDocument.head).createChild({
25730 rel : 'stylesheet',
25739 removeStylesheets : function()
25743 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25748 setStyle : function(style)
25750 Roo.get(this.iframe.contentDocument.head).createChild({
25759 // hide stuff that is not compatible
25773 * @event specialkey
25777 * @cfg {String} fieldClass @hide
25780 * @cfg {String} focusClass @hide
25783 * @cfg {String} autoCreate @hide
25786 * @cfg {String} inputType @hide
25789 * @cfg {String} invalidClass @hide
25792 * @cfg {String} invalidText @hide
25795 * @cfg {String} msgFx @hide
25798 * @cfg {String} validateOnBlur @hide
25802 Roo.HtmlEditorCore.white = [
25803 'area', 'br', 'img', 'input', 'hr', 'wbr',
25805 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25806 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25807 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25808 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25809 'table', 'ul', 'xmp',
25811 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25814 'dir', 'menu', 'ol', 'ul', 'dl',
25820 Roo.HtmlEditorCore.black = [
25821 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25823 'base', 'basefont', 'bgsound', 'blink', 'body',
25824 'frame', 'frameset', 'head', 'html', 'ilayer',
25825 'iframe', 'layer', 'link', 'meta', 'object',
25826 'script', 'style' ,'title', 'xml' // clean later..
25828 Roo.HtmlEditorCore.clean = [
25829 'script', 'style', 'title', 'xml'
25831 Roo.HtmlEditorCore.remove = [
25836 Roo.HtmlEditorCore.ablack = [
25840 Roo.HtmlEditorCore.aclean = [
25841 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25845 Roo.HtmlEditorCore.pwhite= [
25846 'http', 'https', 'mailto'
25849 // white listed style attributes.
25850 Roo.HtmlEditorCore.cwhite= [
25851 // 'text-align', /// default is to allow most things..
25857 // black listed style attributes.
25858 Roo.HtmlEditorCore.cblack= [
25859 // 'font-size' -- this can be set by the project
25863 Roo.HtmlEditorCore.swapCodes =[
25864 [ 8211, "–" ],
25865 [ 8212, "—" ],
25882 * @class Roo.bootstrap.HtmlEditor
25883 * @extends Roo.bootstrap.TextArea
25884 * Bootstrap HtmlEditor class
25887 * Create a new HtmlEditor
25888 * @param {Object} config The config object
25891 Roo.bootstrap.HtmlEditor = function(config){
25892 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25893 if (!this.toolbars) {
25894 this.toolbars = [];
25897 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25900 * @event initialize
25901 * Fires when the editor is fully initialized (including the iframe)
25902 * @param {HtmlEditor} this
25907 * Fires when the editor is first receives the focus. Any insertion must wait
25908 * until after this event.
25909 * @param {HtmlEditor} this
25913 * @event beforesync
25914 * Fires before the textarea is updated with content from the editor iframe. Return false
25915 * to cancel the sync.
25916 * @param {HtmlEditor} this
25917 * @param {String} html
25921 * @event beforepush
25922 * Fires before the iframe editor is updated with content from the textarea. Return false
25923 * to cancel the push.
25924 * @param {HtmlEditor} this
25925 * @param {String} html
25930 * Fires when the textarea is updated with content from the editor iframe.
25931 * @param {HtmlEditor} this
25932 * @param {String} html
25937 * Fires when the iframe editor is updated with content from the textarea.
25938 * @param {HtmlEditor} this
25939 * @param {String} html
25943 * @event editmodechange
25944 * Fires when the editor switches edit modes
25945 * @param {HtmlEditor} this
25946 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25948 editmodechange: true,
25950 * @event editorevent
25951 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25952 * @param {HtmlEditor} this
25956 * @event firstfocus
25957 * Fires when on first focus - needed by toolbars..
25958 * @param {HtmlEditor} this
25963 * Auto save the htmlEditor value as a file into Events
25964 * @param {HtmlEditor} this
25968 * @event savedpreview
25969 * preview the saved version of htmlEditor
25970 * @param {HtmlEditor} this
25977 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25981 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25986 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25991 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25996 * @cfg {Number} height (in pixels)
26000 * @cfg {Number} width (in pixels)
26005 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26008 stylesheets: false,
26013 // private properties
26014 validationEvent : false,
26016 initialized : false,
26019 onFocus : Roo.emptyFn,
26021 hideMode:'offsets',
26023 tbContainer : false,
26027 toolbarContainer :function() {
26028 return this.wrap.select('.x-html-editor-tb',true).first();
26032 * Protected method that will not generally be called directly. It
26033 * is called when the editor creates its toolbar. Override this method if you need to
26034 * add custom toolbar buttons.
26035 * @param {HtmlEditor} editor
26037 createToolbar : function(){
26038 Roo.log('renewing');
26039 Roo.log("create toolbars");
26041 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26042 this.toolbars[0].render(this.toolbarContainer());
26046 // if (!editor.toolbars || !editor.toolbars.length) {
26047 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26050 // for (var i =0 ; i < editor.toolbars.length;i++) {
26051 // editor.toolbars[i] = Roo.factory(
26052 // typeof(editor.toolbars[i]) == 'string' ?
26053 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26054 // Roo.bootstrap.HtmlEditor);
26055 // editor.toolbars[i].init(editor);
26061 onRender : function(ct, position)
26063 // Roo.log("Call onRender: " + this.xtype);
26065 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26067 this.wrap = this.inputEl().wrap({
26068 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26071 this.editorcore.onRender(ct, position);
26073 if (this.resizable) {
26074 this.resizeEl = new Roo.Resizable(this.wrap, {
26078 minHeight : this.height,
26079 height: this.height,
26080 handles : this.resizable,
26083 resize : function(r, w, h) {
26084 _t.onResize(w,h); // -something
26090 this.createToolbar(this);
26093 if(!this.width && this.resizable){
26094 this.setSize(this.wrap.getSize());
26096 if (this.resizeEl) {
26097 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26098 // should trigger onReize..
26104 onResize : function(w, h)
26106 Roo.log('resize: ' +w + ',' + h );
26107 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26111 if(this.inputEl() ){
26112 if(typeof w == 'number'){
26113 var aw = w - this.wrap.getFrameWidth('lr');
26114 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26117 if(typeof h == 'number'){
26118 var tbh = -11; // fixme it needs to tool bar size!
26119 for (var i =0; i < this.toolbars.length;i++) {
26120 // fixme - ask toolbars for heights?
26121 tbh += this.toolbars[i].el.getHeight();
26122 //if (this.toolbars[i].footer) {
26123 // tbh += this.toolbars[i].footer.el.getHeight();
26131 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26132 ah -= 5; // knock a few pixes off for look..
26133 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26137 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26138 this.editorcore.onResize(ew,eh);
26143 * Toggles the editor between standard and source edit mode.
26144 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26146 toggleSourceEdit : function(sourceEditMode)
26148 this.editorcore.toggleSourceEdit(sourceEditMode);
26150 if(this.editorcore.sourceEditMode){
26151 Roo.log('editor - showing textarea');
26154 // Roo.log(this.syncValue());
26156 this.inputEl().removeClass(['hide', 'x-hidden']);
26157 this.inputEl().dom.removeAttribute('tabIndex');
26158 this.inputEl().focus();
26160 Roo.log('editor - hiding textarea');
26162 // Roo.log(this.pushValue());
26165 this.inputEl().addClass(['hide', 'x-hidden']);
26166 this.inputEl().dom.setAttribute('tabIndex', -1);
26167 //this.deferFocus();
26170 if(this.resizable){
26171 this.setSize(this.wrap.getSize());
26174 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26177 // private (for BoxComponent)
26178 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26180 // private (for BoxComponent)
26181 getResizeEl : function(){
26185 // private (for BoxComponent)
26186 getPositionEl : function(){
26191 initEvents : function(){
26192 this.originalValue = this.getValue();
26196 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26199 // markInvalid : Roo.emptyFn,
26201 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26204 // clearInvalid : Roo.emptyFn,
26206 setValue : function(v){
26207 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26208 this.editorcore.pushValue();
26213 deferFocus : function(){
26214 this.focus.defer(10, this);
26218 focus : function(){
26219 this.editorcore.focus();
26225 onDestroy : function(){
26231 for (var i =0; i < this.toolbars.length;i++) {
26232 // fixme - ask toolbars for heights?
26233 this.toolbars[i].onDestroy();
26236 this.wrap.dom.innerHTML = '';
26237 this.wrap.remove();
26242 onFirstFocus : function(){
26243 //Roo.log("onFirstFocus");
26244 this.editorcore.onFirstFocus();
26245 for (var i =0; i < this.toolbars.length;i++) {
26246 this.toolbars[i].onFirstFocus();
26252 syncValue : function()
26254 this.editorcore.syncValue();
26257 pushValue : function()
26259 this.editorcore.pushValue();
26263 // hide stuff that is not compatible
26277 * @event specialkey
26281 * @cfg {String} fieldClass @hide
26284 * @cfg {String} focusClass @hide
26287 * @cfg {String} autoCreate @hide
26290 * @cfg {String} inputType @hide
26294 * @cfg {String} invalidText @hide
26297 * @cfg {String} msgFx @hide
26300 * @cfg {String} validateOnBlur @hide
26309 Roo.namespace('Roo.bootstrap.htmleditor');
26311 * @class Roo.bootstrap.HtmlEditorToolbar1
26317 new Roo.bootstrap.HtmlEditor({
26320 new Roo.bootstrap.HtmlEditorToolbar1({
26321 disable : { fonts: 1 , format: 1, ..., ... , ...],
26327 * @cfg {Object} disable List of elements to disable..
26328 * @cfg {Array} btns List of additional buttons.
26332 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26335 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26338 Roo.apply(this, config);
26340 // default disabled, based on 'good practice'..
26341 this.disable = this.disable || {};
26342 Roo.applyIf(this.disable, {
26345 specialElements : true
26347 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26349 this.editor = config.editor;
26350 this.editorcore = config.editor.editorcore;
26352 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26354 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26355 // dont call parent... till later.
26357 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26362 editorcore : false,
26367 "h1","h2","h3","h4","h5","h6",
26369 "abbr", "acronym", "address", "cite", "samp", "var",
26373 onRender : function(ct, position)
26375 // Roo.log("Call onRender: " + this.xtype);
26377 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26379 this.el.dom.style.marginBottom = '0';
26381 var editorcore = this.editorcore;
26382 var editor= this.editor;
26385 var btn = function(id,cmd , toggle, handler, html){
26387 var event = toggle ? 'toggle' : 'click';
26392 xns: Roo.bootstrap,
26396 enableToggle:toggle !== false,
26398 pressed : toggle ? false : null,
26401 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26402 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26408 // var cb_box = function...
26413 xns: Roo.bootstrap,
26418 xns: Roo.bootstrap,
26422 Roo.each(this.formats, function(f) {
26423 style.menu.items.push({
26425 xns: Roo.bootstrap,
26426 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26431 editorcore.insertTag(this.tagname);
26438 children.push(style);
26440 btn('bold',false,true);
26441 btn('italic',false,true);
26442 btn('align-left', 'justifyleft',true);
26443 btn('align-center', 'justifycenter',true);
26444 btn('align-right' , 'justifyright',true);
26445 btn('link', false, false, function(btn) {
26446 //Roo.log("create link?");
26447 var url = prompt(this.createLinkText, this.defaultLinkValue);
26448 if(url && url != 'http:/'+'/'){
26449 this.editorcore.relayCmd('createlink', url);
26452 btn('list','insertunorderedlist',true);
26453 btn('pencil', false,true, function(btn){
26455 this.toggleSourceEdit(btn.pressed);
26458 if (this.editor.btns.length > 0) {
26459 for (var i = 0; i<this.editor.btns.length; i++) {
26460 children.push(this.editor.btns[i]);
26468 xns: Roo.bootstrap,
26473 xns: Roo.bootstrap,
26478 cog.menu.items.push({
26480 xns: Roo.bootstrap,
26481 html : Clean styles,
26486 editorcore.insertTag(this.tagname);
26495 this.xtype = 'NavSimplebar';
26497 for(var i=0;i< children.length;i++) {
26499 this.buttons.add(this.addxtypeChild(children[i]));
26503 editor.on('editorevent', this.updateToolbar, this);
26505 onBtnClick : function(id)
26507 this.editorcore.relayCmd(id);
26508 this.editorcore.focus();
26512 * Protected method that will not generally be called directly. It triggers
26513 * a toolbar update by reading the markup state of the current selection in the editor.
26515 updateToolbar: function(){
26517 if(!this.editorcore.activated){
26518 this.editor.onFirstFocus(); // is this neeed?
26522 var btns = this.buttons;
26523 var doc = this.editorcore.doc;
26524 btns.get('bold').setActive(doc.queryCommandState('bold'));
26525 btns.get('italic').setActive(doc.queryCommandState('italic'));
26526 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26528 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26529 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26530 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26532 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26533 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26536 var ans = this.editorcore.getAllAncestors();
26537 if (this.formatCombo) {
26540 var store = this.formatCombo.store;
26541 this.formatCombo.setValue("");
26542 for (var i =0; i < ans.length;i++) {
26543 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26545 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26553 // hides menus... - so this cant be on a menu...
26554 Roo.bootstrap.MenuMgr.hideAll();
26556 Roo.bootstrap.MenuMgr.hideAll();
26557 //this.editorsyncValue();
26559 onFirstFocus: function() {
26560 this.buttons.each(function(item){
26564 toggleSourceEdit : function(sourceEditMode){
26567 if(sourceEditMode){
26568 Roo.log("disabling buttons");
26569 this.buttons.each( function(item){
26570 if(item.cmd != 'pencil'){
26576 Roo.log("enabling buttons");
26577 if(this.editorcore.initialized){
26578 this.buttons.each( function(item){
26584 Roo.log("calling toggole on editor");
26585 // tell the editor that it's been pressed..
26586 this.editor.toggleSourceEdit(sourceEditMode);
26600 * @class Roo.bootstrap.Markdown
26601 * @extends Roo.bootstrap.TextArea
26602 * Bootstrap Showdown editable area
26603 * @cfg {string} content
26606 * Create a new Showdown
26609 Roo.bootstrap.Markdown = function(config){
26610 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26614 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26618 initEvents : function()
26621 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26622 this.markdownEl = this.el.createChild({
26623 cls : 'roo-markdown-area'
26625 this.inputEl().addClass('d-none');
26626 if (this.getValue() == '') {
26627 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26630 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26632 this.markdownEl.on('click', this.toggleTextEdit, this);
26633 this.on('blur', this.toggleTextEdit, this);
26634 this.on('specialkey', this.resizeTextArea, this);
26637 toggleTextEdit : function()
26639 var sh = this.markdownEl.getHeight();
26640 this.inputEl().addClass('d-none');
26641 this.markdownEl.addClass('d-none');
26642 if (!this.editing) {
26644 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26645 this.inputEl().removeClass('d-none');
26646 this.inputEl().focus();
26647 this.editing = true;
26650 // show showdown...
26651 this.updateMarkdown();
26652 this.markdownEl.removeClass('d-none');
26653 this.editing = false;
26656 updateMarkdown : function()
26658 if (this.getValue() == '') {
26659 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26663 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26666 resizeTextArea: function () {
26669 Roo.log([sh, this.getValue().split("\n").length * 30]);
26670 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26672 setValue : function(val)
26674 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26675 if (!this.editing) {
26676 this.updateMarkdown();
26682 if (!this.editing) {
26683 this.toggleTextEdit();
26691 * @class Roo.bootstrap.Table.AbstractSelectionModel
26692 * @extends Roo.util.Observable
26693 * Abstract base class for grid SelectionModels. It provides the interface that should be
26694 * implemented by descendant classes. This class should not be directly instantiated.
26697 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26698 this.locked = false;
26699 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26703 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26704 /** @ignore Called by the grid automatically. Do not call directly. */
26705 init : function(grid){
26711 * Locks the selections.
26714 this.locked = true;
26718 * Unlocks the selections.
26720 unlock : function(){
26721 this.locked = false;
26725 * Returns true if the selections are locked.
26726 * @return {Boolean}
26728 isLocked : function(){
26729 return this.locked;
26733 initEvents : function ()
26739 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26740 * @class Roo.bootstrap.Table.RowSelectionModel
26741 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26742 * It supports multiple selections and keyboard selection/navigation.
26744 * @param {Object} config
26747 Roo.bootstrap.Table.RowSelectionModel = function(config){
26748 Roo.apply(this, config);
26749 this.selections = new Roo.util.MixedCollection(false, function(o){
26754 this.lastActive = false;
26758 * @event selectionchange
26759 * Fires when the selection changes
26760 * @param {SelectionModel} this
26762 "selectionchange" : true,
26764 * @event afterselectionchange
26765 * Fires after the selection changes (eg. by key press or clicking)
26766 * @param {SelectionModel} this
26768 "afterselectionchange" : true,
26770 * @event beforerowselect
26771 * Fires when a row is selected being selected, return false to cancel.
26772 * @param {SelectionModel} this
26773 * @param {Number} rowIndex The selected index
26774 * @param {Boolean} keepExisting False if other selections will be cleared
26776 "beforerowselect" : true,
26779 * Fires when a row is selected.
26780 * @param {SelectionModel} this
26781 * @param {Number} rowIndex The selected index
26782 * @param {Roo.data.Record} r The record
26784 "rowselect" : true,
26786 * @event rowdeselect
26787 * Fires when a row is deselected.
26788 * @param {SelectionModel} this
26789 * @param {Number} rowIndex The selected index
26791 "rowdeselect" : true
26793 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26794 this.locked = false;
26797 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26799 * @cfg {Boolean} singleSelect
26800 * True to allow selection of only one row at a time (defaults to false)
26802 singleSelect : false,
26805 initEvents : function()
26808 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26809 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26810 //}else{ // allow click to work like normal
26811 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26813 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26814 this.grid.on("rowclick", this.handleMouseDown, this);
26816 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26817 "up" : function(e){
26819 this.selectPrevious(e.shiftKey);
26820 }else if(this.last !== false && this.lastActive !== false){
26821 var last = this.last;
26822 this.selectRange(this.last, this.lastActive-1);
26823 this.grid.getView().focusRow(this.lastActive);
26824 if(last !== false){
26828 this.selectFirstRow();
26830 this.fireEvent("afterselectionchange", this);
26832 "down" : function(e){
26834 this.selectNext(e.shiftKey);
26835 }else if(this.last !== false && this.lastActive !== false){
26836 var last = this.last;
26837 this.selectRange(this.last, this.lastActive+1);
26838 this.grid.getView().focusRow(this.lastActive);
26839 if(last !== false){
26843 this.selectFirstRow();
26845 this.fireEvent("afterselectionchange", this);
26849 this.grid.store.on('load', function(){
26850 this.selections.clear();
26853 var view = this.grid.view;
26854 view.on("refresh", this.onRefresh, this);
26855 view.on("rowupdated", this.onRowUpdated, this);
26856 view.on("rowremoved", this.onRemove, this);
26861 onRefresh : function()
26863 var ds = this.grid.store, i, v = this.grid.view;
26864 var s = this.selections;
26865 s.each(function(r){
26866 if((i = ds.indexOfId(r.id)) != -1){
26875 onRemove : function(v, index, r){
26876 this.selections.remove(r);
26880 onRowUpdated : function(v, index, r){
26881 if(this.isSelected(r)){
26882 v.onRowSelect(index);
26888 * @param {Array} records The records to select
26889 * @param {Boolean} keepExisting (optional) True to keep existing selections
26891 selectRecords : function(records, keepExisting)
26894 this.clearSelections();
26896 var ds = this.grid.store;
26897 for(var i = 0, len = records.length; i < len; i++){
26898 this.selectRow(ds.indexOf(records[i]), true);
26903 * Gets the number of selected rows.
26906 getCount : function(){
26907 return this.selections.length;
26911 * Selects the first row in the grid.
26913 selectFirstRow : function(){
26918 * Select the last row.
26919 * @param {Boolean} keepExisting (optional) True to keep existing selections
26921 selectLastRow : function(keepExisting){
26922 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26923 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26927 * Selects the row immediately following the last selected row.
26928 * @param {Boolean} keepExisting (optional) True to keep existing selections
26930 selectNext : function(keepExisting)
26932 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26933 this.selectRow(this.last+1, keepExisting);
26934 this.grid.getView().focusRow(this.last);
26939 * Selects the row that precedes the last selected row.
26940 * @param {Boolean} keepExisting (optional) True to keep existing selections
26942 selectPrevious : function(keepExisting){
26944 this.selectRow(this.last-1, keepExisting);
26945 this.grid.getView().focusRow(this.last);
26950 * Returns the selected records
26951 * @return {Array} Array of selected records
26953 getSelections : function(){
26954 return [].concat(this.selections.items);
26958 * Returns the first selected record.
26961 getSelected : function(){
26962 return this.selections.itemAt(0);
26967 * Clears all selections.
26969 clearSelections : function(fast)
26975 var ds = this.grid.store;
26976 var s = this.selections;
26977 s.each(function(r){
26978 this.deselectRow(ds.indexOfId(r.id));
26982 this.selections.clear();
26989 * Selects all rows.
26991 selectAll : function(){
26995 this.selections.clear();
26996 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26997 this.selectRow(i, true);
27002 * Returns True if there is a selection.
27003 * @return {Boolean}
27005 hasSelection : function(){
27006 return this.selections.length > 0;
27010 * Returns True if the specified row is selected.
27011 * @param {Number/Record} record The record or index of the record to check
27012 * @return {Boolean}
27014 isSelected : function(index){
27015 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27016 return (r && this.selections.key(r.id) ? true : false);
27020 * Returns True if the specified record id is selected.
27021 * @param {String} id The id of record to check
27022 * @return {Boolean}
27024 isIdSelected : function(id){
27025 return (this.selections.key(id) ? true : false);
27030 handleMouseDBClick : function(e, t){
27034 handleMouseDown : function(e, t)
27036 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27037 if(this.isLocked() || rowIndex < 0 ){
27040 if(e.shiftKey && this.last !== false){
27041 var last = this.last;
27042 this.selectRange(last, rowIndex, e.ctrlKey);
27043 this.last = last; // reset the last
27047 var isSelected = this.isSelected(rowIndex);
27048 //Roo.log("select row:" + rowIndex);
27050 this.deselectRow(rowIndex);
27052 this.selectRow(rowIndex, true);
27056 if(e.button !== 0 && isSelected){
27057 alert('rowIndex 2: ' + rowIndex);
27058 view.focusRow(rowIndex);
27059 }else if(e.ctrlKey && isSelected){
27060 this.deselectRow(rowIndex);
27061 }else if(!isSelected){
27062 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27063 view.focusRow(rowIndex);
27067 this.fireEvent("afterselectionchange", this);
27070 handleDragableRowClick : function(grid, rowIndex, e)
27072 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27073 this.selectRow(rowIndex, false);
27074 grid.view.focusRow(rowIndex);
27075 this.fireEvent("afterselectionchange", this);
27080 * Selects multiple rows.
27081 * @param {Array} rows Array of the indexes of the row to select
27082 * @param {Boolean} keepExisting (optional) True to keep existing selections
27084 selectRows : function(rows, keepExisting){
27086 this.clearSelections();
27088 for(var i = 0, len = rows.length; i < len; i++){
27089 this.selectRow(rows[i], true);
27094 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27095 * @param {Number} startRow The index of the first row in the range
27096 * @param {Number} endRow The index of the last row in the range
27097 * @param {Boolean} keepExisting (optional) True to retain existing selections
27099 selectRange : function(startRow, endRow, keepExisting){
27104 this.clearSelections();
27106 if(startRow <= endRow){
27107 for(var i = startRow; i <= endRow; i++){
27108 this.selectRow(i, true);
27111 for(var i = startRow; i >= endRow; i--){
27112 this.selectRow(i, true);
27118 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27119 * @param {Number} startRow The index of the first row in the range
27120 * @param {Number} endRow The index of the last row in the range
27122 deselectRange : function(startRow, endRow, preventViewNotify){
27126 for(var i = startRow; i <= endRow; i++){
27127 this.deselectRow(i, preventViewNotify);
27133 * @param {Number} row The index of the row to select
27134 * @param {Boolean} keepExisting (optional) True to keep existing selections
27136 selectRow : function(index, keepExisting, preventViewNotify)
27138 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27141 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27142 if(!keepExisting || this.singleSelect){
27143 this.clearSelections();
27146 var r = this.grid.store.getAt(index);
27147 //console.log('selectRow - record id :' + r.id);
27149 this.selections.add(r);
27150 this.last = this.lastActive = index;
27151 if(!preventViewNotify){
27152 var proxy = new Roo.Element(
27153 this.grid.getRowDom(index)
27155 proxy.addClass('bg-info info');
27157 this.fireEvent("rowselect", this, index, r);
27158 this.fireEvent("selectionchange", this);
27164 * @param {Number} row The index of the row to deselect
27166 deselectRow : function(index, preventViewNotify)
27171 if(this.last == index){
27174 if(this.lastActive == index){
27175 this.lastActive = false;
27178 var r = this.grid.store.getAt(index);
27183 this.selections.remove(r);
27184 //.console.log('deselectRow - record id :' + r.id);
27185 if(!preventViewNotify){
27187 var proxy = new Roo.Element(
27188 this.grid.getRowDom(index)
27190 proxy.removeClass('bg-info info');
27192 this.fireEvent("rowdeselect", this, index);
27193 this.fireEvent("selectionchange", this);
27197 restoreLast : function(){
27199 this.last = this._last;
27204 acceptsNav : function(row, col, cm){
27205 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27209 onEditorKey : function(field, e){
27210 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27215 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27217 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27219 }else if(k == e.ENTER && !e.ctrlKey){
27223 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27225 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27227 }else if(k == e.ESC){
27231 g.startEditing(newCell[0], newCell[1]);
27237 * Ext JS Library 1.1.1
27238 * Copyright(c) 2006-2007, Ext JS, LLC.
27240 * Originally Released Under LGPL - original licence link has changed is not relivant.
27243 * <script type="text/javascript">
27247 * @class Roo.bootstrap.PagingToolbar
27248 * @extends Roo.bootstrap.NavSimplebar
27249 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27251 * Create a new PagingToolbar
27252 * @param {Object} config The config object
27253 * @param {Roo.data.Store} store
27255 Roo.bootstrap.PagingToolbar = function(config)
27257 // old args format still supported... - xtype is prefered..
27258 // created from xtype...
27260 this.ds = config.dataSource;
27262 if (config.store && !this.ds) {
27263 this.store= Roo.factory(config.store, Roo.data);
27264 this.ds = this.store;
27265 this.ds.xmodule = this.xmodule || false;
27268 this.toolbarItems = [];
27269 if (config.items) {
27270 this.toolbarItems = config.items;
27273 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27278 this.bind(this.ds);
27281 if (Roo.bootstrap.version == 4) {
27282 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27284 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27289 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27291 * @cfg {Roo.data.Store} dataSource
27292 * The underlying data store providing the paged data
27295 * @cfg {String/HTMLElement/Element} container
27296 * container The id or element that will contain the toolbar
27299 * @cfg {Boolean} displayInfo
27300 * True to display the displayMsg (defaults to false)
27303 * @cfg {Number} pageSize
27304 * The number of records to display per page (defaults to 20)
27308 * @cfg {String} displayMsg
27309 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27311 displayMsg : 'Displaying {0} - {1} of {2}',
27313 * @cfg {String} emptyMsg
27314 * The message to display when no records are found (defaults to "No data to display")
27316 emptyMsg : 'No data to display',
27318 * Customizable piece of the default paging text (defaults to "Page")
27321 beforePageText : "Page",
27323 * Customizable piece of the default paging text (defaults to "of %0")
27326 afterPageText : "of {0}",
27328 * Customizable piece of the default paging text (defaults to "First Page")
27331 firstText : "First Page",
27333 * Customizable piece of the default paging text (defaults to "Previous Page")
27336 prevText : "Previous Page",
27338 * Customizable piece of the default paging text (defaults to "Next Page")
27341 nextText : "Next Page",
27343 * Customizable piece of the default paging text (defaults to "Last Page")
27346 lastText : "Last Page",
27348 * Customizable piece of the default paging text (defaults to "Refresh")
27351 refreshText : "Refresh",
27355 onRender : function(ct, position)
27357 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27358 this.navgroup.parentId = this.id;
27359 this.navgroup.onRender(this.el, null);
27360 // add the buttons to the navgroup
27362 if(this.displayInfo){
27363 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27364 this.displayEl = this.el.select('.x-paging-info', true).first();
27365 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27366 // this.displayEl = navel.el.select('span',true).first();
27372 Roo.each(_this.buttons, function(e){ // this might need to use render????
27373 Roo.factory(e).render(_this.el);
27377 Roo.each(_this.toolbarItems, function(e) {
27378 _this.navgroup.addItem(e);
27382 this.first = this.navgroup.addItem({
27383 tooltip: this.firstText,
27384 cls: "prev btn-outline-secondary",
27385 html : ' <i class="fa fa-step-backward"></i>',
27387 preventDefault: true,
27388 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27391 this.prev = this.navgroup.addItem({
27392 tooltip: this.prevText,
27393 cls: "prev btn-outline-secondary",
27394 html : ' <i class="fa fa-backward"></i>',
27396 preventDefault: true,
27397 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27399 //this.addSeparator();
27402 var field = this.navgroup.addItem( {
27404 cls : 'x-paging-position btn-outline-secondary',
27406 html : this.beforePageText +
27407 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27408 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27411 this.field = field.el.select('input', true).first();
27412 this.field.on("keydown", this.onPagingKeydown, this);
27413 this.field.on("focus", function(){this.dom.select();});
27416 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27417 //this.field.setHeight(18);
27418 //this.addSeparator();
27419 this.next = this.navgroup.addItem({
27420 tooltip: this.nextText,
27421 cls: "next btn-outline-secondary",
27422 html : ' <i class="fa fa-forward"></i>',
27424 preventDefault: true,
27425 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27427 this.last = this.navgroup.addItem({
27428 tooltip: this.lastText,
27429 html : ' <i class="fa fa-step-forward"></i>',
27430 cls: "next btn-outline-secondary",
27432 preventDefault: true,
27433 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27435 //this.addSeparator();
27436 this.loading = this.navgroup.addItem({
27437 tooltip: this.refreshText,
27438 cls: "btn-outline-secondary",
27439 html : ' <i class="fa fa-refresh"></i>',
27440 preventDefault: true,
27441 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27447 updateInfo : function(){
27448 if(this.displayEl){
27449 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27450 var msg = count == 0 ?
27454 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27456 this.displayEl.update(msg);
27461 onLoad : function(ds, r, o)
27463 this.cursor = o.params && o.params.start ? o.params.start : 0;
27465 var d = this.getPageData(),
27470 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27471 this.field.dom.value = ap;
27472 this.first.setDisabled(ap == 1);
27473 this.prev.setDisabled(ap == 1);
27474 this.next.setDisabled(ap == ps);
27475 this.last.setDisabled(ap == ps);
27476 this.loading.enable();
27481 getPageData : function(){
27482 var total = this.ds.getTotalCount();
27485 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27486 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27491 onLoadError : function(){
27492 this.loading.enable();
27496 onPagingKeydown : function(e){
27497 var k = e.getKey();
27498 var d = this.getPageData();
27500 var v = this.field.dom.value, pageNum;
27501 if(!v || isNaN(pageNum = parseInt(v, 10))){
27502 this.field.dom.value = d.activePage;
27505 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27506 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27509 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))
27511 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27512 this.field.dom.value = pageNum;
27513 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27516 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27518 var v = this.field.dom.value, pageNum;
27519 var increment = (e.shiftKey) ? 10 : 1;
27520 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27523 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27524 this.field.dom.value = d.activePage;
27527 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27529 this.field.dom.value = parseInt(v, 10) + increment;
27530 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27531 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27538 beforeLoad : function(){
27540 this.loading.disable();
27545 onClick : function(which){
27554 ds.load({params:{start: 0, limit: this.pageSize}});
27557 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27560 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27563 var total = ds.getTotalCount();
27564 var extra = total % this.pageSize;
27565 var lastStart = extra ? (total - extra) : total-this.pageSize;
27566 ds.load({params:{start: lastStart, limit: this.pageSize}});
27569 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27575 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27576 * @param {Roo.data.Store} store The data store to unbind
27578 unbind : function(ds){
27579 ds.un("beforeload", this.beforeLoad, this);
27580 ds.un("load", this.onLoad, this);
27581 ds.un("loadexception", this.onLoadError, this);
27582 ds.un("remove", this.updateInfo, this);
27583 ds.un("add", this.updateInfo, this);
27584 this.ds = undefined;
27588 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27589 * @param {Roo.data.Store} store The data store to bind
27591 bind : function(ds){
27592 ds.on("beforeload", this.beforeLoad, this);
27593 ds.on("load", this.onLoad, this);
27594 ds.on("loadexception", this.onLoadError, this);
27595 ds.on("remove", this.updateInfo, this);
27596 ds.on("add", this.updateInfo, this);
27607 * @class Roo.bootstrap.MessageBar
27608 * @extends Roo.bootstrap.Component
27609 * Bootstrap MessageBar class
27610 * @cfg {String} html contents of the MessageBar
27611 * @cfg {String} weight (info | success | warning | danger) default info
27612 * @cfg {String} beforeClass insert the bar before the given class
27613 * @cfg {Boolean} closable (true | false) default false
27614 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27617 * Create a new Element
27618 * @param {Object} config The config object
27621 Roo.bootstrap.MessageBar = function(config){
27622 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27625 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27631 beforeClass: 'bootstrap-sticky-wrap',
27633 getAutoCreate : function(){
27637 cls: 'alert alert-dismissable alert-' + this.weight,
27642 html: this.html || ''
27648 cfg.cls += ' alert-messages-fixed';
27662 onRender : function(ct, position)
27664 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27667 var cfg = Roo.apply({}, this.getAutoCreate());
27671 cfg.cls += ' ' + this.cls;
27674 cfg.style = this.style;
27676 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27678 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27681 this.el.select('>button.close').on('click', this.hide, this);
27687 if (!this.rendered) {
27693 this.fireEvent('show', this);
27699 if (!this.rendered) {
27705 this.fireEvent('hide', this);
27708 update : function()
27710 // var e = this.el.dom.firstChild;
27712 // if(this.closable){
27713 // e = e.nextSibling;
27716 // e.data = this.html || '';
27718 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27734 * @class Roo.bootstrap.Graph
27735 * @extends Roo.bootstrap.Component
27736 * Bootstrap Graph class
27740 @cfg {String} graphtype bar | vbar | pie
27741 @cfg {number} g_x coodinator | centre x (pie)
27742 @cfg {number} g_y coodinator | centre y (pie)
27743 @cfg {number} g_r radius (pie)
27744 @cfg {number} g_height height of the chart (respected by all elements in the set)
27745 @cfg {number} g_width width of the chart (respected by all elements in the set)
27746 @cfg {Object} title The title of the chart
27749 -opts (object) options for the chart
27751 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27752 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27754 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.
27755 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27757 o stretch (boolean)
27759 -opts (object) options for the pie
27762 o startAngle (number)
27763 o endAngle (number)
27767 * Create a new Input
27768 * @param {Object} config The config object
27771 Roo.bootstrap.Graph = function(config){
27772 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27778 * The img click event for the img.
27779 * @param {Roo.EventObject} e
27785 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27796 //g_colors: this.colors,
27803 getAutoCreate : function(){
27814 onRender : function(ct,position){
27817 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27819 if (typeof(Raphael) == 'undefined') {
27820 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27824 this.raphael = Raphael(this.el.dom);
27826 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27827 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27828 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27829 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27831 r.text(160, 10, "Single Series Chart").attr(txtattr);
27832 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27833 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27834 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27836 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27837 r.barchart(330, 10, 300, 220, data1);
27838 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27839 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27842 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27843 // r.barchart(30, 30, 560, 250, xdata, {
27844 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27845 // axis : "0 0 1 1",
27846 // axisxlabels : xdata
27847 // //yvalues : cols,
27850 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27852 // this.load(null,xdata,{
27853 // axis : "0 0 1 1",
27854 // axisxlabels : xdata
27859 load : function(graphtype,xdata,opts)
27861 this.raphael.clear();
27863 graphtype = this.graphtype;
27868 var r = this.raphael,
27869 fin = function () {
27870 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27872 fout = function () {
27873 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27875 pfin = function() {
27876 this.sector.stop();
27877 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27880 this.label[0].stop();
27881 this.label[0].attr({ r: 7.5 });
27882 this.label[1].attr({ "font-weight": 800 });
27885 pfout = function() {
27886 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27889 this.label[0].animate({ r: 5 }, 500, "bounce");
27890 this.label[1].attr({ "font-weight": 400 });
27896 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27899 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27902 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27903 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27905 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27912 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27917 setTitle: function(o)
27922 initEvents: function() {
27925 this.el.on('click', this.onClick, this);
27929 onClick : function(e)
27931 Roo.log('img onclick');
27932 this.fireEvent('click', this, e);
27944 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27947 * @class Roo.bootstrap.dash.NumberBox
27948 * @extends Roo.bootstrap.Component
27949 * Bootstrap NumberBox class
27950 * @cfg {String} headline Box headline
27951 * @cfg {String} content Box content
27952 * @cfg {String} icon Box icon
27953 * @cfg {String} footer Footer text
27954 * @cfg {String} fhref Footer href
27957 * Create a new NumberBox
27958 * @param {Object} config The config object
27962 Roo.bootstrap.dash.NumberBox = function(config){
27963 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27967 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27976 getAutoCreate : function(){
27980 cls : 'small-box ',
27988 cls : 'roo-headline',
27989 html : this.headline
27993 cls : 'roo-content',
27994 html : this.content
28008 cls : 'ion ' + this.icon
28017 cls : 'small-box-footer',
28018 href : this.fhref || '#',
28022 cfg.cn.push(footer);
28029 onRender : function(ct,position){
28030 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28037 setHeadline: function (value)
28039 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28042 setFooter: function (value, href)
28044 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28047 this.el.select('a.small-box-footer',true).first().attr('href', href);
28052 setContent: function (value)
28054 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28057 initEvents: function()
28071 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28074 * @class Roo.bootstrap.dash.TabBox
28075 * @extends Roo.bootstrap.Component
28076 * Bootstrap TabBox class
28077 * @cfg {String} title Title of the TabBox
28078 * @cfg {String} icon Icon of the TabBox
28079 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28080 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28083 * Create a new TabBox
28084 * @param {Object} config The config object
28088 Roo.bootstrap.dash.TabBox = function(config){
28089 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28094 * When a pane is added
28095 * @param {Roo.bootstrap.dash.TabPane} pane
28099 * @event activatepane
28100 * When a pane is activated
28101 * @param {Roo.bootstrap.dash.TabPane} pane
28103 "activatepane" : true
28111 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28116 tabScrollable : false,
28118 getChildContainer : function()
28120 return this.el.select('.tab-content', true).first();
28123 getAutoCreate : function(){
28127 cls: 'pull-left header',
28135 cls: 'fa ' + this.icon
28141 cls: 'nav nav-tabs pull-right',
28147 if(this.tabScrollable){
28154 cls: 'nav nav-tabs pull-right',
28165 cls: 'nav-tabs-custom',
28170 cls: 'tab-content no-padding',
28178 initEvents : function()
28180 //Roo.log('add add pane handler');
28181 this.on('addpane', this.onAddPane, this);
28184 * Updates the box title
28185 * @param {String} html to set the title to.
28187 setTitle : function(value)
28189 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28191 onAddPane : function(pane)
28193 this.panes.push(pane);
28194 //Roo.log('addpane');
28196 // tabs are rendere left to right..
28197 if(!this.showtabs){
28201 var ctr = this.el.select('.nav-tabs', true).first();
28204 var existing = ctr.select('.nav-tab',true);
28205 var qty = existing.getCount();;
28208 var tab = ctr.createChild({
28210 cls : 'nav-tab' + (qty ? '' : ' active'),
28218 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28221 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28223 pane.el.addClass('active');
28228 onTabClick : function(ev,un,ob,pane)
28230 //Roo.log('tab - prev default');
28231 ev.preventDefault();
28234 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28235 pane.tab.addClass('active');
28236 //Roo.log(pane.title);
28237 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28238 // technically we should have a deactivate event.. but maybe add later.
28239 // and it should not de-activate the selected tab...
28240 this.fireEvent('activatepane', pane);
28241 pane.el.addClass('active');
28242 pane.fireEvent('activate');
28247 getActivePane : function()
28250 Roo.each(this.panes, function(p) {
28251 if(p.el.hasClass('active')){
28272 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28274 * @class Roo.bootstrap.TabPane
28275 * @extends Roo.bootstrap.Component
28276 * Bootstrap TabPane class
28277 * @cfg {Boolean} active (false | true) Default false
28278 * @cfg {String} title title of panel
28282 * Create a new TabPane
28283 * @param {Object} config The config object
28286 Roo.bootstrap.dash.TabPane = function(config){
28287 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28293 * When a pane is activated
28294 * @param {Roo.bootstrap.dash.TabPane} pane
28301 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28306 // the tabBox that this is attached to.
28309 getAutoCreate : function()
28317 cfg.cls += ' active';
28322 initEvents : function()
28324 //Roo.log('trigger add pane handler');
28325 this.parent().fireEvent('addpane', this)
28329 * Updates the tab title
28330 * @param {String} html to set the title to.
28332 setTitle: function(str)
28338 this.tab.select('a', true).first().dom.innerHTML = str;
28355 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28358 * @class Roo.bootstrap.menu.Menu
28359 * @extends Roo.bootstrap.Component
28360 * Bootstrap Menu class - container for Menu
28361 * @cfg {String} html Text of the menu
28362 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28363 * @cfg {String} icon Font awesome icon
28364 * @cfg {String} pos Menu align to (top | bottom) default bottom
28368 * Create a new Menu
28369 * @param {Object} config The config object
28373 Roo.bootstrap.menu.Menu = function(config){
28374 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28378 * @event beforeshow
28379 * Fires before this menu is displayed
28380 * @param {Roo.bootstrap.menu.Menu} this
28384 * @event beforehide
28385 * Fires before this menu is hidden
28386 * @param {Roo.bootstrap.menu.Menu} this
28391 * Fires after this menu is displayed
28392 * @param {Roo.bootstrap.menu.Menu} this
28397 * Fires after this menu is hidden
28398 * @param {Roo.bootstrap.menu.Menu} this
28403 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28404 * @param {Roo.bootstrap.menu.Menu} this
28405 * @param {Roo.EventObject} e
28412 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28416 weight : 'default',
28421 getChildContainer : function() {
28422 if(this.isSubMenu){
28426 return this.el.select('ul.dropdown-menu', true).first();
28429 getAutoCreate : function()
28434 cls : 'roo-menu-text',
28442 cls : 'fa ' + this.icon
28453 cls : 'dropdown-button btn btn-' + this.weight,
28458 cls : 'dropdown-toggle btn btn-' + this.weight,
28468 cls : 'dropdown-menu'
28474 if(this.pos == 'top'){
28475 cfg.cls += ' dropup';
28478 if(this.isSubMenu){
28481 cls : 'dropdown-menu'
28488 onRender : function(ct, position)
28490 this.isSubMenu = ct.hasClass('dropdown-submenu');
28492 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28495 initEvents : function()
28497 if(this.isSubMenu){
28501 this.hidden = true;
28503 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28504 this.triggerEl.on('click', this.onTriggerPress, this);
28506 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28507 this.buttonEl.on('click', this.onClick, this);
28513 if(this.isSubMenu){
28517 return this.el.select('ul.dropdown-menu', true).first();
28520 onClick : function(e)
28522 this.fireEvent("click", this, e);
28525 onTriggerPress : function(e)
28527 if (this.isVisible()) {
28534 isVisible : function(){
28535 return !this.hidden;
28540 this.fireEvent("beforeshow", this);
28542 this.hidden = false;
28543 this.el.addClass('open');
28545 Roo.get(document).on("mouseup", this.onMouseUp, this);
28547 this.fireEvent("show", this);
28554 this.fireEvent("beforehide", this);
28556 this.hidden = true;
28557 this.el.removeClass('open');
28559 Roo.get(document).un("mouseup", this.onMouseUp);
28561 this.fireEvent("hide", this);
28564 onMouseUp : function()
28578 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28581 * @class Roo.bootstrap.menu.Item
28582 * @extends Roo.bootstrap.Component
28583 * Bootstrap MenuItem class
28584 * @cfg {Boolean} submenu (true | false) default false
28585 * @cfg {String} html text of the item
28586 * @cfg {String} href the link
28587 * @cfg {Boolean} disable (true | false) default false
28588 * @cfg {Boolean} preventDefault (true | false) default true
28589 * @cfg {String} icon Font awesome icon
28590 * @cfg {String} pos Submenu align to (left | right) default right
28594 * Create a new Item
28595 * @param {Object} config The config object
28599 Roo.bootstrap.menu.Item = function(config){
28600 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28604 * Fires when the mouse is hovering over this menu
28605 * @param {Roo.bootstrap.menu.Item} this
28606 * @param {Roo.EventObject} e
28611 * Fires when the mouse exits this menu
28612 * @param {Roo.bootstrap.menu.Item} this
28613 * @param {Roo.EventObject} e
28619 * The raw click event for the entire grid.
28620 * @param {Roo.EventObject} e
28626 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28631 preventDefault: true,
28636 getAutoCreate : function()
28641 cls : 'roo-menu-item-text',
28649 cls : 'fa ' + this.icon
28658 href : this.href || '#',
28665 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28669 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28671 if(this.pos == 'left'){
28672 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28679 initEvents : function()
28681 this.el.on('mouseover', this.onMouseOver, this);
28682 this.el.on('mouseout', this.onMouseOut, this);
28684 this.el.select('a', true).first().on('click', this.onClick, this);
28688 onClick : function(e)
28690 if(this.preventDefault){
28691 e.preventDefault();
28694 this.fireEvent("click", this, e);
28697 onMouseOver : function(e)
28699 if(this.submenu && this.pos == 'left'){
28700 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28703 this.fireEvent("mouseover", this, e);
28706 onMouseOut : function(e)
28708 this.fireEvent("mouseout", this, e);
28720 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28723 * @class Roo.bootstrap.menu.Separator
28724 * @extends Roo.bootstrap.Component
28725 * Bootstrap Separator class
28728 * Create a new Separator
28729 * @param {Object} config The config object
28733 Roo.bootstrap.menu.Separator = function(config){
28734 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28737 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28739 getAutoCreate : function(){
28760 * @class Roo.bootstrap.Tooltip
28761 * Bootstrap Tooltip class
28762 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28763 * to determine which dom element triggers the tooltip.
28765 * It needs to add support for additional attributes like tooltip-position
28768 * Create a new Toolti
28769 * @param {Object} config The config object
28772 Roo.bootstrap.Tooltip = function(config){
28773 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28775 this.alignment = Roo.bootstrap.Tooltip.alignment;
28777 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28778 this.alignment = config.alignment;
28783 Roo.apply(Roo.bootstrap.Tooltip, {
28785 * @function init initialize tooltip monitoring.
28789 currentTip : false,
28790 currentRegion : false,
28796 Roo.get(document).on('mouseover', this.enter ,this);
28797 Roo.get(document).on('mouseout', this.leave, this);
28800 this.currentTip = new Roo.bootstrap.Tooltip();
28803 enter : function(ev)
28805 var dom = ev.getTarget();
28807 //Roo.log(['enter',dom]);
28808 var el = Roo.fly(dom);
28809 if (this.currentEl) {
28811 //Roo.log(this.currentEl);
28812 //Roo.log(this.currentEl.contains(dom));
28813 if (this.currentEl == el) {
28816 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28822 if (this.currentTip.el) {
28823 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28827 if(!el || el.dom == document){
28833 // you can not look for children, as if el is the body.. then everythign is the child..
28834 if (!el.attr('tooltip')) { //
28835 if (!el.select("[tooltip]").elements.length) {
28838 // is the mouse over this child...?
28839 bindEl = el.select("[tooltip]").first();
28840 var xy = ev.getXY();
28841 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28842 //Roo.log("not in region.");
28845 //Roo.log("child element over..");
28848 this.currentEl = bindEl;
28849 this.currentTip.bind(bindEl);
28850 this.currentRegion = Roo.lib.Region.getRegion(dom);
28851 this.currentTip.enter();
28854 leave : function(ev)
28856 var dom = ev.getTarget();
28857 //Roo.log(['leave',dom]);
28858 if (!this.currentEl) {
28863 if (dom != this.currentEl.dom) {
28866 var xy = ev.getXY();
28867 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28870 // only activate leave if mouse cursor is outside... bounding box..
28875 if (this.currentTip) {
28876 this.currentTip.leave();
28878 //Roo.log('clear currentEl');
28879 this.currentEl = false;
28884 'left' : ['r-l', [-2,0], 'right'],
28885 'right' : ['l-r', [2,0], 'left'],
28886 'bottom' : ['t-b', [0,2], 'top'],
28887 'top' : [ 'b-t', [0,-2], 'bottom']
28893 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28898 delay : null, // can be { show : 300 , hide: 500}
28902 hoverState : null, //???
28904 placement : 'bottom',
28908 getAutoCreate : function(){
28915 cls : 'tooltip-arrow arrow'
28918 cls : 'tooltip-inner'
28925 bind : function(el)
28930 initEvents : function()
28932 this.arrowEl = this.el.select('.arrow', true).first();
28933 this.innerEl = this.el.select('.tooltip-inner', true).first();
28936 enter : function () {
28938 if (this.timeout != null) {
28939 clearTimeout(this.timeout);
28942 this.hoverState = 'in';
28943 //Roo.log("enter - show");
28944 if (!this.delay || !this.delay.show) {
28949 this.timeout = setTimeout(function () {
28950 if (_t.hoverState == 'in') {
28953 }, this.delay.show);
28957 clearTimeout(this.timeout);
28959 this.hoverState = 'out';
28960 if (!this.delay || !this.delay.hide) {
28966 this.timeout = setTimeout(function () {
28967 //Roo.log("leave - timeout");
28969 if (_t.hoverState == 'out') {
28971 Roo.bootstrap.Tooltip.currentEl = false;
28976 show : function (msg)
28979 this.render(document.body);
28982 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28984 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28986 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28988 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28989 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28991 var placement = typeof this.placement == 'function' ?
28992 this.placement.call(this, this.el, on_el) :
28995 var autoToken = /\s?auto?\s?/i;
28996 var autoPlace = autoToken.test(placement);
28998 placement = placement.replace(autoToken, '') || 'top';
29002 //this.el.setXY([0,0]);
29004 //this.el.dom.style.display='block';
29006 //this.el.appendTo(on_el);
29008 var p = this.getPosition();
29009 var box = this.el.getBox();
29015 var align = this.alignment[placement];
29017 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29019 if(placement == 'top' || placement == 'bottom'){
29021 placement = 'right';
29024 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29025 placement = 'left';
29028 var scroll = Roo.select('body', true).first().getScroll();
29030 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29034 align = this.alignment[placement];
29036 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29040 this.el.alignTo(this.bindEl, align[0],align[1]);
29041 //var arrow = this.el.select('.arrow',true).first();
29042 //arrow.set(align[2],
29044 this.el.addClass(placement);
29045 this.el.addClass("bs-tooltip-"+ placement);
29047 this.el.addClass('in fade show');
29049 this.hoverState = null;
29051 if (this.el.hasClass('fade')) {
29066 //this.el.setXY([0,0]);
29067 this.el.removeClass(['show', 'in']);
29083 * @class Roo.bootstrap.LocationPicker
29084 * @extends Roo.bootstrap.Component
29085 * Bootstrap LocationPicker class
29086 * @cfg {Number} latitude Position when init default 0
29087 * @cfg {Number} longitude Position when init default 0
29088 * @cfg {Number} zoom default 15
29089 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29090 * @cfg {Boolean} mapTypeControl default false
29091 * @cfg {Boolean} disableDoubleClickZoom default false
29092 * @cfg {Boolean} scrollwheel default true
29093 * @cfg {Boolean} streetViewControl default false
29094 * @cfg {Number} radius default 0
29095 * @cfg {String} locationName
29096 * @cfg {Boolean} draggable default true
29097 * @cfg {Boolean} enableAutocomplete default false
29098 * @cfg {Boolean} enableReverseGeocode default true
29099 * @cfg {String} markerTitle
29102 * Create a new LocationPicker
29103 * @param {Object} config The config object
29107 Roo.bootstrap.LocationPicker = function(config){
29109 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29114 * Fires when the picker initialized.
29115 * @param {Roo.bootstrap.LocationPicker} this
29116 * @param {Google Location} location
29120 * @event positionchanged
29121 * Fires when the picker position changed.
29122 * @param {Roo.bootstrap.LocationPicker} this
29123 * @param {Google Location} location
29125 positionchanged : true,
29128 * Fires when the map resize.
29129 * @param {Roo.bootstrap.LocationPicker} this
29134 * Fires when the map show.
29135 * @param {Roo.bootstrap.LocationPicker} this
29140 * Fires when the map hide.
29141 * @param {Roo.bootstrap.LocationPicker} this
29146 * Fires when click the map.
29147 * @param {Roo.bootstrap.LocationPicker} this
29148 * @param {Map event} e
29152 * @event mapRightClick
29153 * Fires when right click the map.
29154 * @param {Roo.bootstrap.LocationPicker} this
29155 * @param {Map event} e
29157 mapRightClick : true,
29159 * @event markerClick
29160 * Fires when click the marker.
29161 * @param {Roo.bootstrap.LocationPicker} this
29162 * @param {Map event} e
29164 markerClick : true,
29166 * @event markerRightClick
29167 * Fires when right click the marker.
29168 * @param {Roo.bootstrap.LocationPicker} this
29169 * @param {Map event} e
29171 markerRightClick : true,
29173 * @event OverlayViewDraw
29174 * Fires when OverlayView Draw
29175 * @param {Roo.bootstrap.LocationPicker} this
29177 OverlayViewDraw : true,
29179 * @event OverlayViewOnAdd
29180 * Fires when OverlayView Draw
29181 * @param {Roo.bootstrap.LocationPicker} this
29183 OverlayViewOnAdd : true,
29185 * @event OverlayViewOnRemove
29186 * Fires when OverlayView Draw
29187 * @param {Roo.bootstrap.LocationPicker} this
29189 OverlayViewOnRemove : true,
29191 * @event OverlayViewShow
29192 * Fires when OverlayView Draw
29193 * @param {Roo.bootstrap.LocationPicker} this
29194 * @param {Pixel} cpx
29196 OverlayViewShow : true,
29198 * @event OverlayViewHide
29199 * Fires when OverlayView Draw
29200 * @param {Roo.bootstrap.LocationPicker} this
29202 OverlayViewHide : true,
29204 * @event loadexception
29205 * Fires when load google lib failed.
29206 * @param {Roo.bootstrap.LocationPicker} this
29208 loadexception : true
29213 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29215 gMapContext: false,
29221 mapTypeControl: false,
29222 disableDoubleClickZoom: false,
29224 streetViewControl: false,
29228 enableAutocomplete: false,
29229 enableReverseGeocode: true,
29232 getAutoCreate: function()
29237 cls: 'roo-location-picker'
29243 initEvents: function(ct, position)
29245 if(!this.el.getWidth() || this.isApplied()){
29249 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29254 initial: function()
29256 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29257 this.fireEvent('loadexception', this);
29261 if(!this.mapTypeId){
29262 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29265 this.gMapContext = this.GMapContext();
29267 this.initOverlayView();
29269 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29273 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29274 _this.setPosition(_this.gMapContext.marker.position);
29277 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29278 _this.fireEvent('mapClick', this, event);
29282 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29283 _this.fireEvent('mapRightClick', this, event);
29287 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29288 _this.fireEvent('markerClick', this, event);
29292 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29293 _this.fireEvent('markerRightClick', this, event);
29297 this.setPosition(this.gMapContext.location);
29299 this.fireEvent('initial', this, this.gMapContext.location);
29302 initOverlayView: function()
29306 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29310 _this.fireEvent('OverlayViewDraw', _this);
29315 _this.fireEvent('OverlayViewOnAdd', _this);
29318 onRemove: function()
29320 _this.fireEvent('OverlayViewOnRemove', _this);
29323 show: function(cpx)
29325 _this.fireEvent('OverlayViewShow', _this, cpx);
29330 _this.fireEvent('OverlayViewHide', _this);
29336 fromLatLngToContainerPixel: function(event)
29338 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29341 isApplied: function()
29343 return this.getGmapContext() == false ? false : true;
29346 getGmapContext: function()
29348 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29351 GMapContext: function()
29353 var position = new google.maps.LatLng(this.latitude, this.longitude);
29355 var _map = new google.maps.Map(this.el.dom, {
29358 mapTypeId: this.mapTypeId,
29359 mapTypeControl: this.mapTypeControl,
29360 disableDoubleClickZoom: this.disableDoubleClickZoom,
29361 scrollwheel: this.scrollwheel,
29362 streetViewControl: this.streetViewControl,
29363 locationName: this.locationName,
29364 draggable: this.draggable,
29365 enableAutocomplete: this.enableAutocomplete,
29366 enableReverseGeocode: this.enableReverseGeocode
29369 var _marker = new google.maps.Marker({
29370 position: position,
29372 title: this.markerTitle,
29373 draggable: this.draggable
29380 location: position,
29381 radius: this.radius,
29382 locationName: this.locationName,
29383 addressComponents: {
29384 formatted_address: null,
29385 addressLine1: null,
29386 addressLine2: null,
29388 streetNumber: null,
29392 stateOrProvince: null
29395 domContainer: this.el.dom,
29396 geodecoder: new google.maps.Geocoder()
29400 drawCircle: function(center, radius, options)
29402 if (this.gMapContext.circle != null) {
29403 this.gMapContext.circle.setMap(null);
29407 options = Roo.apply({}, options, {
29408 strokeColor: "#0000FF",
29409 strokeOpacity: .35,
29411 fillColor: "#0000FF",
29415 options.map = this.gMapContext.map;
29416 options.radius = radius;
29417 options.center = center;
29418 this.gMapContext.circle = new google.maps.Circle(options);
29419 return this.gMapContext.circle;
29425 setPosition: function(location)
29427 this.gMapContext.location = location;
29428 this.gMapContext.marker.setPosition(location);
29429 this.gMapContext.map.panTo(location);
29430 this.drawCircle(location, this.gMapContext.radius, {});
29434 if (this.gMapContext.settings.enableReverseGeocode) {
29435 this.gMapContext.geodecoder.geocode({
29436 latLng: this.gMapContext.location
29437 }, function(results, status) {
29439 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29440 _this.gMapContext.locationName = results[0].formatted_address;
29441 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29443 _this.fireEvent('positionchanged', this, location);
29450 this.fireEvent('positionchanged', this, location);
29455 google.maps.event.trigger(this.gMapContext.map, "resize");
29457 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29459 this.fireEvent('resize', this);
29462 setPositionByLatLng: function(latitude, longitude)
29464 this.setPosition(new google.maps.LatLng(latitude, longitude));
29467 getCurrentPosition: function()
29470 latitude: this.gMapContext.location.lat(),
29471 longitude: this.gMapContext.location.lng()
29475 getAddressName: function()
29477 return this.gMapContext.locationName;
29480 getAddressComponents: function()
29482 return this.gMapContext.addressComponents;
29485 address_component_from_google_geocode: function(address_components)
29489 for (var i = 0; i < address_components.length; i++) {
29490 var component = address_components[i];
29491 if (component.types.indexOf("postal_code") >= 0) {
29492 result.postalCode = component.short_name;
29493 } else if (component.types.indexOf("street_number") >= 0) {
29494 result.streetNumber = component.short_name;
29495 } else if (component.types.indexOf("route") >= 0) {
29496 result.streetName = component.short_name;
29497 } else if (component.types.indexOf("neighborhood") >= 0) {
29498 result.city = component.short_name;
29499 } else if (component.types.indexOf("locality") >= 0) {
29500 result.city = component.short_name;
29501 } else if (component.types.indexOf("sublocality") >= 0) {
29502 result.district = component.short_name;
29503 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29504 result.stateOrProvince = component.short_name;
29505 } else if (component.types.indexOf("country") >= 0) {
29506 result.country = component.short_name;
29510 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29511 result.addressLine2 = "";
29515 setZoomLevel: function(zoom)
29517 this.gMapContext.map.setZoom(zoom);
29530 this.fireEvent('show', this);
29541 this.fireEvent('hide', this);
29546 Roo.apply(Roo.bootstrap.LocationPicker, {
29548 OverlayView : function(map, options)
29550 options = options || {};
29557 * @class Roo.bootstrap.Alert
29558 * @extends Roo.bootstrap.Component
29559 * Bootstrap Alert class - shows an alert area box
29561 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29562 Enter a valid email address
29565 * @cfg {String} title The title of alert
29566 * @cfg {String} html The content of alert
29567 * @cfg {String} weight ( success | info | warning | danger )
29568 * @cfg {String} faicon font-awesomeicon
29571 * Create a new alert
29572 * @param {Object} config The config object
29576 Roo.bootstrap.Alert = function(config){
29577 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29581 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29588 getAutoCreate : function()
29597 cls : 'roo-alert-icon'
29602 cls : 'roo-alert-title',
29607 cls : 'roo-alert-text',
29614 cfg.cn[0].cls += ' fa ' + this.faicon;
29618 cfg.cls += ' alert-' + this.weight;
29624 initEvents: function()
29626 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29629 setTitle : function(str)
29631 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29634 setText : function(str)
29636 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29639 setWeight : function(weight)
29642 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29645 this.weight = weight;
29647 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29650 setIcon : function(icon)
29653 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29656 this.faicon = icon;
29658 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29679 * @class Roo.bootstrap.UploadCropbox
29680 * @extends Roo.bootstrap.Component
29681 * Bootstrap UploadCropbox class
29682 * @cfg {String} emptyText show when image has been loaded
29683 * @cfg {String} rotateNotify show when image too small to rotate
29684 * @cfg {Number} errorTimeout default 3000
29685 * @cfg {Number} minWidth default 300
29686 * @cfg {Number} minHeight default 300
29687 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29688 * @cfg {Boolean} isDocument (true|false) default false
29689 * @cfg {String} url action url
29690 * @cfg {String} paramName default 'imageUpload'
29691 * @cfg {String} method default POST
29692 * @cfg {Boolean} loadMask (true|false) default true
29693 * @cfg {Boolean} loadingText default 'Loading...'
29696 * Create a new UploadCropbox
29697 * @param {Object} config The config object
29700 Roo.bootstrap.UploadCropbox = function(config){
29701 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29705 * @event beforeselectfile
29706 * Fire before select file
29707 * @param {Roo.bootstrap.UploadCropbox} this
29709 "beforeselectfile" : true,
29712 * Fire after initEvent
29713 * @param {Roo.bootstrap.UploadCropbox} this
29718 * Fire after initEvent
29719 * @param {Roo.bootstrap.UploadCropbox} this
29720 * @param {String} data
29725 * Fire when preparing the file data
29726 * @param {Roo.bootstrap.UploadCropbox} this
29727 * @param {Object} file
29732 * Fire when get exception
29733 * @param {Roo.bootstrap.UploadCropbox} this
29734 * @param {XMLHttpRequest} xhr
29736 "exception" : true,
29738 * @event beforeloadcanvas
29739 * Fire before load the canvas
29740 * @param {Roo.bootstrap.UploadCropbox} this
29741 * @param {String} src
29743 "beforeloadcanvas" : true,
29746 * Fire when trash image
29747 * @param {Roo.bootstrap.UploadCropbox} this
29752 * Fire when download the image
29753 * @param {Roo.bootstrap.UploadCropbox} this
29757 * @event footerbuttonclick
29758 * Fire when footerbuttonclick
29759 * @param {Roo.bootstrap.UploadCropbox} this
29760 * @param {String} type
29762 "footerbuttonclick" : true,
29766 * @param {Roo.bootstrap.UploadCropbox} this
29771 * Fire when rotate the image
29772 * @param {Roo.bootstrap.UploadCropbox} this
29773 * @param {String} pos
29778 * Fire when inspect the file
29779 * @param {Roo.bootstrap.UploadCropbox} this
29780 * @param {Object} file
29785 * Fire when xhr upload the file
29786 * @param {Roo.bootstrap.UploadCropbox} this
29787 * @param {Object} data
29792 * Fire when arrange the file data
29793 * @param {Roo.bootstrap.UploadCropbox} this
29794 * @param {Object} formData
29799 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29802 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29804 emptyText : 'Click to upload image',
29805 rotateNotify : 'Image is too small to rotate',
29806 errorTimeout : 3000,
29820 cropType : 'image/jpeg',
29822 canvasLoaded : false,
29823 isDocument : false,
29825 paramName : 'imageUpload',
29827 loadingText : 'Loading...',
29830 getAutoCreate : function()
29834 cls : 'roo-upload-cropbox',
29838 cls : 'roo-upload-cropbox-selector',
29843 cls : 'roo-upload-cropbox-body',
29844 style : 'cursor:pointer',
29848 cls : 'roo-upload-cropbox-preview'
29852 cls : 'roo-upload-cropbox-thumb'
29856 cls : 'roo-upload-cropbox-empty-notify',
29857 html : this.emptyText
29861 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29862 html : this.rotateNotify
29868 cls : 'roo-upload-cropbox-footer',
29871 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29881 onRender : function(ct, position)
29883 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29885 if (this.buttons.length) {
29887 Roo.each(this.buttons, function(bb) {
29889 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29891 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29897 this.maskEl = this.el;
29901 initEvents : function()
29903 this.urlAPI = (window.createObjectURL && window) ||
29904 (window.URL && URL.revokeObjectURL && URL) ||
29905 (window.webkitURL && webkitURL);
29907 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29908 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29910 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29911 this.selectorEl.hide();
29913 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29914 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29916 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29917 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29918 this.thumbEl.hide();
29920 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29921 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29923 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29924 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29925 this.errorEl.hide();
29927 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29928 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29929 this.footerEl.hide();
29931 this.setThumbBoxSize();
29937 this.fireEvent('initial', this);
29944 window.addEventListener("resize", function() { _this.resize(); } );
29946 this.bodyEl.on('click', this.beforeSelectFile, this);
29949 this.bodyEl.on('touchstart', this.onTouchStart, this);
29950 this.bodyEl.on('touchmove', this.onTouchMove, this);
29951 this.bodyEl.on('touchend', this.onTouchEnd, this);
29955 this.bodyEl.on('mousedown', this.onMouseDown, this);
29956 this.bodyEl.on('mousemove', this.onMouseMove, this);
29957 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29958 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29959 Roo.get(document).on('mouseup', this.onMouseUp, this);
29962 this.selectorEl.on('change', this.onFileSelected, this);
29968 this.baseScale = 1;
29970 this.baseRotate = 1;
29971 this.dragable = false;
29972 this.pinching = false;
29975 this.cropData = false;
29976 this.notifyEl.dom.innerHTML = this.emptyText;
29978 this.selectorEl.dom.value = '';
29982 resize : function()
29984 if(this.fireEvent('resize', this) != false){
29985 this.setThumbBoxPosition();
29986 this.setCanvasPosition();
29990 onFooterButtonClick : function(e, el, o, type)
29993 case 'rotate-left' :
29994 this.onRotateLeft(e);
29996 case 'rotate-right' :
29997 this.onRotateRight(e);
30000 this.beforeSelectFile(e);
30015 this.fireEvent('footerbuttonclick', this, type);
30018 beforeSelectFile : function(e)
30020 e.preventDefault();
30022 if(this.fireEvent('beforeselectfile', this) != false){
30023 this.selectorEl.dom.click();
30027 onFileSelected : function(e)
30029 e.preventDefault();
30031 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30035 var file = this.selectorEl.dom.files[0];
30037 if(this.fireEvent('inspect', this, file) != false){
30038 this.prepare(file);
30043 trash : function(e)
30045 this.fireEvent('trash', this);
30048 download : function(e)
30050 this.fireEvent('download', this);
30053 loadCanvas : function(src)
30055 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30059 this.imageEl = document.createElement('img');
30063 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30065 this.imageEl.src = src;
30069 onLoadCanvas : function()
30071 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30072 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30074 this.bodyEl.un('click', this.beforeSelectFile, this);
30076 this.notifyEl.hide();
30077 this.thumbEl.show();
30078 this.footerEl.show();
30080 this.baseRotateLevel();
30082 if(this.isDocument){
30083 this.setThumbBoxSize();
30086 this.setThumbBoxPosition();
30088 this.baseScaleLevel();
30094 this.canvasLoaded = true;
30097 this.maskEl.unmask();
30102 setCanvasPosition : function()
30104 if(!this.canvasEl){
30108 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30109 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30111 this.previewEl.setLeft(pw);
30112 this.previewEl.setTop(ph);
30116 onMouseDown : function(e)
30120 this.dragable = true;
30121 this.pinching = false;
30123 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30124 this.dragable = false;
30128 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30129 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30133 onMouseMove : function(e)
30137 if(!this.canvasLoaded){
30141 if (!this.dragable){
30145 var minX = Math.ceil(this.thumbEl.getLeft(true));
30146 var minY = Math.ceil(this.thumbEl.getTop(true));
30148 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30149 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30151 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30152 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30154 x = x - this.mouseX;
30155 y = y - this.mouseY;
30157 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30158 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30160 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30161 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30163 this.previewEl.setLeft(bgX);
30164 this.previewEl.setTop(bgY);
30166 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30167 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30170 onMouseUp : function(e)
30174 this.dragable = false;
30177 onMouseWheel : function(e)
30181 this.startScale = this.scale;
30183 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30185 if(!this.zoomable()){
30186 this.scale = this.startScale;
30195 zoomable : function()
30197 var minScale = this.thumbEl.getWidth() / this.minWidth;
30199 if(this.minWidth < this.minHeight){
30200 minScale = this.thumbEl.getHeight() / this.minHeight;
30203 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30204 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30208 (this.rotate == 0 || this.rotate == 180) &&
30210 width > this.imageEl.OriginWidth ||
30211 height > this.imageEl.OriginHeight ||
30212 (width < this.minWidth && height < this.minHeight)
30220 (this.rotate == 90 || this.rotate == 270) &&
30222 width > this.imageEl.OriginWidth ||
30223 height > this.imageEl.OriginHeight ||
30224 (width < this.minHeight && height < this.minWidth)
30231 !this.isDocument &&
30232 (this.rotate == 0 || this.rotate == 180) &&
30234 width < this.minWidth ||
30235 width > this.imageEl.OriginWidth ||
30236 height < this.minHeight ||
30237 height > this.imageEl.OriginHeight
30244 !this.isDocument &&
30245 (this.rotate == 90 || this.rotate == 270) &&
30247 width < this.minHeight ||
30248 width > this.imageEl.OriginWidth ||
30249 height < this.minWidth ||
30250 height > this.imageEl.OriginHeight
30260 onRotateLeft : function(e)
30262 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30264 var minScale = this.thumbEl.getWidth() / this.minWidth;
30266 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30267 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30269 this.startScale = this.scale;
30271 while (this.getScaleLevel() < minScale){
30273 this.scale = this.scale + 1;
30275 if(!this.zoomable()){
30280 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30281 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30286 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30293 this.scale = this.startScale;
30295 this.onRotateFail();
30300 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30302 if(this.isDocument){
30303 this.setThumbBoxSize();
30304 this.setThumbBoxPosition();
30305 this.setCanvasPosition();
30310 this.fireEvent('rotate', this, 'left');
30314 onRotateRight : function(e)
30316 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30318 var minScale = this.thumbEl.getWidth() / this.minWidth;
30320 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30321 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30323 this.startScale = this.scale;
30325 while (this.getScaleLevel() < minScale){
30327 this.scale = this.scale + 1;
30329 if(!this.zoomable()){
30334 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30335 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30340 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30347 this.scale = this.startScale;
30349 this.onRotateFail();
30354 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30356 if(this.isDocument){
30357 this.setThumbBoxSize();
30358 this.setThumbBoxPosition();
30359 this.setCanvasPosition();
30364 this.fireEvent('rotate', this, 'right');
30367 onRotateFail : function()
30369 this.errorEl.show(true);
30373 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30378 this.previewEl.dom.innerHTML = '';
30380 var canvasEl = document.createElement("canvas");
30382 var contextEl = canvasEl.getContext("2d");
30384 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30385 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30386 var center = this.imageEl.OriginWidth / 2;
30388 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30389 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30390 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30391 center = this.imageEl.OriginHeight / 2;
30394 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30396 contextEl.translate(center, center);
30397 contextEl.rotate(this.rotate * Math.PI / 180);
30399 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30401 this.canvasEl = document.createElement("canvas");
30403 this.contextEl = this.canvasEl.getContext("2d");
30405 switch (this.rotate) {
30408 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30409 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30411 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30416 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30417 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30419 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30420 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);
30424 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30429 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30430 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30432 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30433 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);
30437 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);
30442 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30443 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30445 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30446 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30450 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);
30457 this.previewEl.appendChild(this.canvasEl);
30459 this.setCanvasPosition();
30464 if(!this.canvasLoaded){
30468 var imageCanvas = document.createElement("canvas");
30470 var imageContext = imageCanvas.getContext("2d");
30472 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30473 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30475 var center = imageCanvas.width / 2;
30477 imageContext.translate(center, center);
30479 imageContext.rotate(this.rotate * Math.PI / 180);
30481 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30483 var canvas = document.createElement("canvas");
30485 var context = canvas.getContext("2d");
30487 canvas.width = this.minWidth;
30488 canvas.height = this.minHeight;
30490 switch (this.rotate) {
30493 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30494 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30496 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30497 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30499 var targetWidth = this.minWidth - 2 * x;
30500 var targetHeight = this.minHeight - 2 * y;
30504 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30505 scale = targetWidth / width;
30508 if(x > 0 && y == 0){
30509 scale = targetHeight / height;
30512 if(x > 0 && y > 0){
30513 scale = targetWidth / width;
30515 if(width < height){
30516 scale = targetHeight / height;
30520 context.scale(scale, scale);
30522 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30523 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30525 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30526 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30528 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30533 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30534 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30536 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30537 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30539 var targetWidth = this.minWidth - 2 * x;
30540 var targetHeight = this.minHeight - 2 * y;
30544 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30545 scale = targetWidth / width;
30548 if(x > 0 && y == 0){
30549 scale = targetHeight / height;
30552 if(x > 0 && y > 0){
30553 scale = targetWidth / width;
30555 if(width < height){
30556 scale = targetHeight / height;
30560 context.scale(scale, scale);
30562 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30563 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30565 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30566 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30568 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30570 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30575 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30576 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30578 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30579 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30581 var targetWidth = this.minWidth - 2 * x;
30582 var targetHeight = this.minHeight - 2 * y;
30586 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30587 scale = targetWidth / width;
30590 if(x > 0 && y == 0){
30591 scale = targetHeight / height;
30594 if(x > 0 && y > 0){
30595 scale = targetWidth / width;
30597 if(width < height){
30598 scale = targetHeight / height;
30602 context.scale(scale, scale);
30604 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30605 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30607 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30608 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30610 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30611 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30613 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30618 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30619 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30621 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30622 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30624 var targetWidth = this.minWidth - 2 * x;
30625 var targetHeight = this.minHeight - 2 * y;
30629 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30630 scale = targetWidth / width;
30633 if(x > 0 && y == 0){
30634 scale = targetHeight / height;
30637 if(x > 0 && y > 0){
30638 scale = targetWidth / width;
30640 if(width < height){
30641 scale = targetHeight / height;
30645 context.scale(scale, scale);
30647 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30648 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30650 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30651 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30653 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30655 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30662 this.cropData = canvas.toDataURL(this.cropType);
30664 if(this.fireEvent('crop', this, this.cropData) !== false){
30665 this.process(this.file, this.cropData);
30672 setThumbBoxSize : function()
30676 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30677 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30678 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30680 this.minWidth = width;
30681 this.minHeight = height;
30683 if(this.rotate == 90 || this.rotate == 270){
30684 this.minWidth = height;
30685 this.minHeight = width;
30690 width = Math.ceil(this.minWidth * height / this.minHeight);
30692 if(this.minWidth > this.minHeight){
30694 height = Math.ceil(this.minHeight * width / this.minWidth);
30697 this.thumbEl.setStyle({
30698 width : width + 'px',
30699 height : height + 'px'
30706 setThumbBoxPosition : function()
30708 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30709 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30711 this.thumbEl.setLeft(x);
30712 this.thumbEl.setTop(y);
30716 baseRotateLevel : function()
30718 this.baseRotate = 1;
30721 typeof(this.exif) != 'undefined' &&
30722 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30723 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30725 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30728 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30732 baseScaleLevel : function()
30736 if(this.isDocument){
30738 if(this.baseRotate == 6 || this.baseRotate == 8){
30740 height = this.thumbEl.getHeight();
30741 this.baseScale = height / this.imageEl.OriginWidth;
30743 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30744 width = this.thumbEl.getWidth();
30745 this.baseScale = width / this.imageEl.OriginHeight;
30751 height = this.thumbEl.getHeight();
30752 this.baseScale = height / this.imageEl.OriginHeight;
30754 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30755 width = this.thumbEl.getWidth();
30756 this.baseScale = width / this.imageEl.OriginWidth;
30762 if(this.baseRotate == 6 || this.baseRotate == 8){
30764 width = this.thumbEl.getHeight();
30765 this.baseScale = width / this.imageEl.OriginHeight;
30767 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30768 height = this.thumbEl.getWidth();
30769 this.baseScale = height / this.imageEl.OriginHeight;
30772 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30773 height = this.thumbEl.getWidth();
30774 this.baseScale = height / this.imageEl.OriginHeight;
30776 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30777 width = this.thumbEl.getHeight();
30778 this.baseScale = width / this.imageEl.OriginWidth;
30785 width = this.thumbEl.getWidth();
30786 this.baseScale = width / this.imageEl.OriginWidth;
30788 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30789 height = this.thumbEl.getHeight();
30790 this.baseScale = height / this.imageEl.OriginHeight;
30793 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30795 height = this.thumbEl.getHeight();
30796 this.baseScale = height / this.imageEl.OriginHeight;
30798 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30799 width = this.thumbEl.getWidth();
30800 this.baseScale = width / this.imageEl.OriginWidth;
30808 getScaleLevel : function()
30810 return this.baseScale * Math.pow(1.1, this.scale);
30813 onTouchStart : function(e)
30815 if(!this.canvasLoaded){
30816 this.beforeSelectFile(e);
30820 var touches = e.browserEvent.touches;
30826 if(touches.length == 1){
30827 this.onMouseDown(e);
30831 if(touches.length != 2){
30837 for(var i = 0, finger; finger = touches[i]; i++){
30838 coords.push(finger.pageX, finger.pageY);
30841 var x = Math.pow(coords[0] - coords[2], 2);
30842 var y = Math.pow(coords[1] - coords[3], 2);
30844 this.startDistance = Math.sqrt(x + y);
30846 this.startScale = this.scale;
30848 this.pinching = true;
30849 this.dragable = false;
30853 onTouchMove : function(e)
30855 if(!this.pinching && !this.dragable){
30859 var touches = e.browserEvent.touches;
30866 this.onMouseMove(e);
30872 for(var i = 0, finger; finger = touches[i]; i++){
30873 coords.push(finger.pageX, finger.pageY);
30876 var x = Math.pow(coords[0] - coords[2], 2);
30877 var y = Math.pow(coords[1] - coords[3], 2);
30879 this.endDistance = Math.sqrt(x + y);
30881 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30883 if(!this.zoomable()){
30884 this.scale = this.startScale;
30892 onTouchEnd : function(e)
30894 this.pinching = false;
30895 this.dragable = false;
30899 process : function(file, crop)
30902 this.maskEl.mask(this.loadingText);
30905 this.xhr = new XMLHttpRequest();
30907 file.xhr = this.xhr;
30909 this.xhr.open(this.method, this.url, true);
30912 "Accept": "application/json",
30913 "Cache-Control": "no-cache",
30914 "X-Requested-With": "XMLHttpRequest"
30917 for (var headerName in headers) {
30918 var headerValue = headers[headerName];
30920 this.xhr.setRequestHeader(headerName, headerValue);
30926 this.xhr.onload = function()
30928 _this.xhrOnLoad(_this.xhr);
30931 this.xhr.onerror = function()
30933 _this.xhrOnError(_this.xhr);
30936 var formData = new FormData();
30938 formData.append('returnHTML', 'NO');
30941 formData.append('crop', crop);
30944 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30945 formData.append(this.paramName, file, file.name);
30948 if(typeof(file.filename) != 'undefined'){
30949 formData.append('filename', file.filename);
30952 if(typeof(file.mimetype) != 'undefined'){
30953 formData.append('mimetype', file.mimetype);
30956 if(this.fireEvent('arrange', this, formData) != false){
30957 this.xhr.send(formData);
30961 xhrOnLoad : function(xhr)
30964 this.maskEl.unmask();
30967 if (xhr.readyState !== 4) {
30968 this.fireEvent('exception', this, xhr);
30972 var response = Roo.decode(xhr.responseText);
30974 if(!response.success){
30975 this.fireEvent('exception', this, xhr);
30979 var response = Roo.decode(xhr.responseText);
30981 this.fireEvent('upload', this, response);
30985 xhrOnError : function()
30988 this.maskEl.unmask();
30991 Roo.log('xhr on error');
30993 var response = Roo.decode(xhr.responseText);
30999 prepare : function(file)
31002 this.maskEl.mask(this.loadingText);
31008 if(typeof(file) === 'string'){
31009 this.loadCanvas(file);
31013 if(!file || !this.urlAPI){
31018 this.cropType = file.type;
31022 if(this.fireEvent('prepare', this, this.file) != false){
31024 var reader = new FileReader();
31026 reader.onload = function (e) {
31027 if (e.target.error) {
31028 Roo.log(e.target.error);
31032 var buffer = e.target.result,
31033 dataView = new DataView(buffer),
31035 maxOffset = dataView.byteLength - 4,
31039 if (dataView.getUint16(0) === 0xffd8) {
31040 while (offset < maxOffset) {
31041 markerBytes = dataView.getUint16(offset);
31043 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31044 markerLength = dataView.getUint16(offset + 2) + 2;
31045 if (offset + markerLength > dataView.byteLength) {
31046 Roo.log('Invalid meta data: Invalid segment size.');
31050 if(markerBytes == 0xffe1){
31051 _this.parseExifData(
31058 offset += markerLength;
31068 var url = _this.urlAPI.createObjectURL(_this.file);
31070 _this.loadCanvas(url);
31075 reader.readAsArrayBuffer(this.file);
31081 parseExifData : function(dataView, offset, length)
31083 var tiffOffset = offset + 10,
31087 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31088 // No Exif data, might be XMP data instead
31092 // Check for the ASCII code for "Exif" (0x45786966):
31093 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31094 // No Exif data, might be XMP data instead
31097 if (tiffOffset + 8 > dataView.byteLength) {
31098 Roo.log('Invalid Exif data: Invalid segment size.');
31101 // Check for the two null bytes:
31102 if (dataView.getUint16(offset + 8) !== 0x0000) {
31103 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31106 // Check the byte alignment:
31107 switch (dataView.getUint16(tiffOffset)) {
31109 littleEndian = true;
31112 littleEndian = false;
31115 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31118 // Check for the TIFF tag marker (0x002A):
31119 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31120 Roo.log('Invalid Exif data: Missing TIFF marker.');
31123 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31124 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31126 this.parseExifTags(
31129 tiffOffset + dirOffset,
31134 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31139 if (dirOffset + 6 > dataView.byteLength) {
31140 Roo.log('Invalid Exif data: Invalid directory offset.');
31143 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31144 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31145 if (dirEndOffset + 4 > dataView.byteLength) {
31146 Roo.log('Invalid Exif data: Invalid directory size.');
31149 for (i = 0; i < tagsNumber; i += 1) {
31153 dirOffset + 2 + 12 * i, // tag offset
31157 // Return the offset to the next directory:
31158 return dataView.getUint32(dirEndOffset, littleEndian);
31161 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31163 var tag = dataView.getUint16(offset, littleEndian);
31165 this.exif[tag] = this.getExifValue(
31169 dataView.getUint16(offset + 2, littleEndian), // tag type
31170 dataView.getUint32(offset + 4, littleEndian), // tag length
31175 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31177 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31186 Roo.log('Invalid Exif data: Invalid tag type.');
31190 tagSize = tagType.size * length;
31191 // Determine if the value is contained in the dataOffset bytes,
31192 // or if the value at the dataOffset is a pointer to the actual data:
31193 dataOffset = tagSize > 4 ?
31194 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31195 if (dataOffset + tagSize > dataView.byteLength) {
31196 Roo.log('Invalid Exif data: Invalid data offset.');
31199 if (length === 1) {
31200 return tagType.getValue(dataView, dataOffset, littleEndian);
31203 for (i = 0; i < length; i += 1) {
31204 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31207 if (tagType.ascii) {
31209 // Concatenate the chars:
31210 for (i = 0; i < values.length; i += 1) {
31212 // Ignore the terminating NULL byte(s):
31213 if (c === '\u0000') {
31225 Roo.apply(Roo.bootstrap.UploadCropbox, {
31227 'Orientation': 0x0112
31231 1: 0, //'top-left',
31233 3: 180, //'bottom-right',
31234 // 4: 'bottom-left',
31236 6: 90, //'right-top',
31237 // 7: 'right-bottom',
31238 8: 270 //'left-bottom'
31242 // byte, 8-bit unsigned int:
31244 getValue: function (dataView, dataOffset) {
31245 return dataView.getUint8(dataOffset);
31249 // ascii, 8-bit byte:
31251 getValue: function (dataView, dataOffset) {
31252 return String.fromCharCode(dataView.getUint8(dataOffset));
31257 // short, 16 bit int:
31259 getValue: function (dataView, dataOffset, littleEndian) {
31260 return dataView.getUint16(dataOffset, littleEndian);
31264 // long, 32 bit int:
31266 getValue: function (dataView, dataOffset, littleEndian) {
31267 return dataView.getUint32(dataOffset, littleEndian);
31271 // rational = two long values, first is numerator, second is denominator:
31273 getValue: function (dataView, dataOffset, littleEndian) {
31274 return dataView.getUint32(dataOffset, littleEndian) /
31275 dataView.getUint32(dataOffset + 4, littleEndian);
31279 // slong, 32 bit signed int:
31281 getValue: function (dataView, dataOffset, littleEndian) {
31282 return dataView.getInt32(dataOffset, littleEndian);
31286 // srational, two slongs, first is numerator, second is denominator:
31288 getValue: function (dataView, dataOffset, littleEndian) {
31289 return dataView.getInt32(dataOffset, littleEndian) /
31290 dataView.getInt32(dataOffset + 4, littleEndian);
31300 cls : 'btn-group roo-upload-cropbox-rotate-left',
31301 action : 'rotate-left',
31305 cls : 'btn btn-default',
31306 html : '<i class="fa fa-undo"></i>'
31312 cls : 'btn-group roo-upload-cropbox-picture',
31313 action : 'picture',
31317 cls : 'btn btn-default',
31318 html : '<i class="fa fa-picture-o"></i>'
31324 cls : 'btn-group roo-upload-cropbox-rotate-right',
31325 action : 'rotate-right',
31329 cls : 'btn btn-default',
31330 html : '<i class="fa fa-repeat"></i>'
31338 cls : 'btn-group roo-upload-cropbox-rotate-left',
31339 action : 'rotate-left',
31343 cls : 'btn btn-default',
31344 html : '<i class="fa fa-undo"></i>'
31350 cls : 'btn-group roo-upload-cropbox-download',
31351 action : 'download',
31355 cls : 'btn btn-default',
31356 html : '<i class="fa fa-download"></i>'
31362 cls : 'btn-group roo-upload-cropbox-crop',
31367 cls : 'btn btn-default',
31368 html : '<i class="fa fa-crop"></i>'
31374 cls : 'btn-group roo-upload-cropbox-trash',
31379 cls : 'btn btn-default',
31380 html : '<i class="fa fa-trash"></i>'
31386 cls : 'btn-group roo-upload-cropbox-rotate-right',
31387 action : 'rotate-right',
31391 cls : 'btn btn-default',
31392 html : '<i class="fa fa-repeat"></i>'
31400 cls : 'btn-group roo-upload-cropbox-rotate-left',
31401 action : 'rotate-left',
31405 cls : 'btn btn-default',
31406 html : '<i class="fa fa-undo"></i>'
31412 cls : 'btn-group roo-upload-cropbox-rotate-right',
31413 action : 'rotate-right',
31417 cls : 'btn btn-default',
31418 html : '<i class="fa fa-repeat"></i>'
31431 * @class Roo.bootstrap.DocumentManager
31432 * @extends Roo.bootstrap.Component
31433 * Bootstrap DocumentManager class
31434 * @cfg {String} paramName default 'imageUpload'
31435 * @cfg {String} toolTipName default 'filename'
31436 * @cfg {String} method default POST
31437 * @cfg {String} url action url
31438 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31439 * @cfg {Boolean} multiple multiple upload default true
31440 * @cfg {Number} thumbSize default 300
31441 * @cfg {String} fieldLabel
31442 * @cfg {Number} labelWidth default 4
31443 * @cfg {String} labelAlign (left|top) default left
31444 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31445 * @cfg {Number} labellg set the width of label (1-12)
31446 * @cfg {Number} labelmd set the width of label (1-12)
31447 * @cfg {Number} labelsm set the width of label (1-12)
31448 * @cfg {Number} labelxs set the width of label (1-12)
31451 * Create a new DocumentManager
31452 * @param {Object} config The config object
31455 Roo.bootstrap.DocumentManager = function(config){
31456 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31459 this.delegates = [];
31464 * Fire when initial the DocumentManager
31465 * @param {Roo.bootstrap.DocumentManager} this
31470 * inspect selected file
31471 * @param {Roo.bootstrap.DocumentManager} this
31472 * @param {File} file
31477 * Fire when xhr load exception
31478 * @param {Roo.bootstrap.DocumentManager} this
31479 * @param {XMLHttpRequest} xhr
31481 "exception" : true,
31483 * @event afterupload
31484 * Fire when xhr load exception
31485 * @param {Roo.bootstrap.DocumentManager} this
31486 * @param {XMLHttpRequest} xhr
31488 "afterupload" : true,
31491 * prepare the form data
31492 * @param {Roo.bootstrap.DocumentManager} this
31493 * @param {Object} formData
31498 * Fire when remove the file
31499 * @param {Roo.bootstrap.DocumentManager} this
31500 * @param {Object} file
31505 * Fire after refresh the file
31506 * @param {Roo.bootstrap.DocumentManager} this
31511 * Fire after click the image
31512 * @param {Roo.bootstrap.DocumentManager} this
31513 * @param {Object} file
31518 * Fire when upload a image and editable set to true
31519 * @param {Roo.bootstrap.DocumentManager} this
31520 * @param {Object} file
31524 * @event beforeselectfile
31525 * Fire before select file
31526 * @param {Roo.bootstrap.DocumentManager} this
31528 "beforeselectfile" : true,
31531 * Fire before process file
31532 * @param {Roo.bootstrap.DocumentManager} this
31533 * @param {Object} file
31537 * @event previewrendered
31538 * Fire when preview rendered
31539 * @param {Roo.bootstrap.DocumentManager} this
31540 * @param {Object} file
31542 "previewrendered" : true,
31545 "previewResize" : true
31550 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31559 paramName : 'imageUpload',
31560 toolTipName : 'filename',
31563 labelAlign : 'left',
31573 getAutoCreate : function()
31575 var managerWidget = {
31577 cls : 'roo-document-manager',
31581 cls : 'roo-document-manager-selector',
31586 cls : 'roo-document-manager-uploader',
31590 cls : 'roo-document-manager-upload-btn',
31591 html : '<i class="fa fa-plus"></i>'
31602 cls : 'column col-md-12',
31607 if(this.fieldLabel.length){
31612 cls : 'column col-md-12',
31613 html : this.fieldLabel
31617 cls : 'column col-md-12',
31622 if(this.labelAlign == 'left'){
31627 html : this.fieldLabel
31636 if(this.labelWidth > 12){
31637 content[0].style = "width: " + this.labelWidth + 'px';
31640 if(this.labelWidth < 13 && this.labelmd == 0){
31641 this.labelmd = this.labelWidth;
31644 if(this.labellg > 0){
31645 content[0].cls += ' col-lg-' + this.labellg;
31646 content[1].cls += ' col-lg-' + (12 - this.labellg);
31649 if(this.labelmd > 0){
31650 content[0].cls += ' col-md-' + this.labelmd;
31651 content[1].cls += ' col-md-' + (12 - this.labelmd);
31654 if(this.labelsm > 0){
31655 content[0].cls += ' col-sm-' + this.labelsm;
31656 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31659 if(this.labelxs > 0){
31660 content[0].cls += ' col-xs-' + this.labelxs;
31661 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31669 cls : 'row clearfix',
31677 initEvents : function()
31679 this.managerEl = this.el.select('.roo-document-manager', true).first();
31680 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31682 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31683 this.selectorEl.hide();
31686 this.selectorEl.attr('multiple', 'multiple');
31689 this.selectorEl.on('change', this.onFileSelected, this);
31691 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31692 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31694 this.uploader.on('click', this.onUploaderClick, this);
31696 this.renderProgressDialog();
31700 window.addEventListener("resize", function() { _this.refresh(); } );
31702 this.fireEvent('initial', this);
31705 renderProgressDialog : function()
31709 this.progressDialog = new Roo.bootstrap.Modal({
31710 cls : 'roo-document-manager-progress-dialog',
31711 allow_close : false,
31722 btnclick : function() {
31723 _this.uploadCancel();
31729 this.progressDialog.render(Roo.get(document.body));
31731 this.progress = new Roo.bootstrap.Progress({
31732 cls : 'roo-document-manager-progress',
31737 this.progress.render(this.progressDialog.getChildContainer());
31739 this.progressBar = new Roo.bootstrap.ProgressBar({
31740 cls : 'roo-document-manager-progress-bar',
31743 aria_valuemax : 12,
31747 this.progressBar.render(this.progress.getChildContainer());
31750 onUploaderClick : function(e)
31752 e.preventDefault();
31754 if(this.fireEvent('beforeselectfile', this) != false){
31755 this.selectorEl.dom.click();
31760 onFileSelected : function(e)
31762 e.preventDefault();
31764 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31768 Roo.each(this.selectorEl.dom.files, function(file){
31769 if(this.fireEvent('inspect', this, file) != false){
31770 this.files.push(file);
31780 this.selectorEl.dom.value = '';
31782 if(!this.files || !this.files.length){
31786 if(this.boxes > 0 && this.files.length > this.boxes){
31787 this.files = this.files.slice(0, this.boxes);
31790 this.uploader.show();
31792 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31793 this.uploader.hide();
31802 Roo.each(this.files, function(file){
31804 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31805 var f = this.renderPreview(file);
31810 if(file.type.indexOf('image') != -1){
31811 this.delegates.push(
31813 _this.process(file);
31814 }).createDelegate(this)
31822 _this.process(file);
31823 }).createDelegate(this)
31828 this.files = files;
31830 this.delegates = this.delegates.concat(docs);
31832 if(!this.delegates.length){
31837 this.progressBar.aria_valuemax = this.delegates.length;
31844 arrange : function()
31846 if(!this.delegates.length){
31847 this.progressDialog.hide();
31852 var delegate = this.delegates.shift();
31854 this.progressDialog.show();
31856 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31858 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31863 refresh : function()
31865 this.uploader.show();
31867 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31868 this.uploader.hide();
31871 Roo.isTouch ? this.closable(false) : this.closable(true);
31873 this.fireEvent('refresh', this);
31876 onRemove : function(e, el, o)
31878 e.preventDefault();
31880 this.fireEvent('remove', this, o);
31884 remove : function(o)
31888 Roo.each(this.files, function(file){
31889 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31898 this.files = files;
31905 Roo.each(this.files, function(file){
31910 file.target.remove();
31919 onClick : function(e, el, o)
31921 e.preventDefault();
31923 this.fireEvent('click', this, o);
31927 closable : function(closable)
31929 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31931 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31943 xhrOnLoad : function(xhr)
31945 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31949 if (xhr.readyState !== 4) {
31951 this.fireEvent('exception', this, xhr);
31955 var response = Roo.decode(xhr.responseText);
31957 if(!response.success){
31959 this.fireEvent('exception', this, xhr);
31963 var file = this.renderPreview(response.data);
31965 this.files.push(file);
31969 this.fireEvent('afterupload', this, xhr);
31973 xhrOnError : function(xhr)
31975 Roo.log('xhr on error');
31977 var response = Roo.decode(xhr.responseText);
31984 process : function(file)
31986 if(this.fireEvent('process', this, file) !== false){
31987 if(this.editable && file.type.indexOf('image') != -1){
31988 this.fireEvent('edit', this, file);
31992 this.uploadStart(file, false);
31999 uploadStart : function(file, crop)
32001 this.xhr = new XMLHttpRequest();
32003 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32008 file.xhr = this.xhr;
32010 this.managerEl.createChild({
32012 cls : 'roo-document-manager-loading',
32016 tooltip : file.name,
32017 cls : 'roo-document-manager-thumb',
32018 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32024 this.xhr.open(this.method, this.url, true);
32027 "Accept": "application/json",
32028 "Cache-Control": "no-cache",
32029 "X-Requested-With": "XMLHttpRequest"
32032 for (var headerName in headers) {
32033 var headerValue = headers[headerName];
32035 this.xhr.setRequestHeader(headerName, headerValue);
32041 this.xhr.onload = function()
32043 _this.xhrOnLoad(_this.xhr);
32046 this.xhr.onerror = function()
32048 _this.xhrOnError(_this.xhr);
32051 var formData = new FormData();
32053 formData.append('returnHTML', 'NO');
32056 formData.append('crop', crop);
32059 formData.append(this.paramName, file, file.name);
32066 if(this.fireEvent('prepare', this, formData, options) != false){
32068 if(options.manually){
32072 this.xhr.send(formData);
32076 this.uploadCancel();
32079 uploadCancel : function()
32085 this.delegates = [];
32087 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32094 renderPreview : function(file)
32096 if(typeof(file.target) != 'undefined' && file.target){
32100 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32102 var previewEl = this.managerEl.createChild({
32104 cls : 'roo-document-manager-preview',
32108 tooltip : file[this.toolTipName],
32109 cls : 'roo-document-manager-thumb',
32110 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32115 html : '<i class="fa fa-times-circle"></i>'
32120 var close = previewEl.select('button.close', true).first();
32122 close.on('click', this.onRemove, this, file);
32124 file.target = previewEl;
32126 var image = previewEl.select('img', true).first();
32130 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32132 image.on('click', this.onClick, this, file);
32134 this.fireEvent('previewrendered', this, file);
32140 onPreviewLoad : function(file, image)
32142 if(typeof(file.target) == 'undefined' || !file.target){
32146 var width = image.dom.naturalWidth || image.dom.width;
32147 var height = image.dom.naturalHeight || image.dom.height;
32149 if(!this.previewResize) {
32153 if(width > height){
32154 file.target.addClass('wide');
32158 file.target.addClass('tall');
32163 uploadFromSource : function(file, crop)
32165 this.xhr = new XMLHttpRequest();
32167 this.managerEl.createChild({
32169 cls : 'roo-document-manager-loading',
32173 tooltip : file.name,
32174 cls : 'roo-document-manager-thumb',
32175 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32181 this.xhr.open(this.method, this.url, true);
32184 "Accept": "application/json",
32185 "Cache-Control": "no-cache",
32186 "X-Requested-With": "XMLHttpRequest"
32189 for (var headerName in headers) {
32190 var headerValue = headers[headerName];
32192 this.xhr.setRequestHeader(headerName, headerValue);
32198 this.xhr.onload = function()
32200 _this.xhrOnLoad(_this.xhr);
32203 this.xhr.onerror = function()
32205 _this.xhrOnError(_this.xhr);
32208 var formData = new FormData();
32210 formData.append('returnHTML', 'NO');
32212 formData.append('crop', crop);
32214 if(typeof(file.filename) != 'undefined'){
32215 formData.append('filename', file.filename);
32218 if(typeof(file.mimetype) != 'undefined'){
32219 formData.append('mimetype', file.mimetype);
32224 if(this.fireEvent('prepare', this, formData) != false){
32225 this.xhr.send(formData);
32235 * @class Roo.bootstrap.DocumentViewer
32236 * @extends Roo.bootstrap.Component
32237 * Bootstrap DocumentViewer class
32238 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32239 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32242 * Create a new DocumentViewer
32243 * @param {Object} config The config object
32246 Roo.bootstrap.DocumentViewer = function(config){
32247 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32252 * Fire after initEvent
32253 * @param {Roo.bootstrap.DocumentViewer} this
32259 * @param {Roo.bootstrap.DocumentViewer} this
32264 * Fire after download button
32265 * @param {Roo.bootstrap.DocumentViewer} this
32270 * Fire after trash button
32271 * @param {Roo.bootstrap.DocumentViewer} this
32278 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32280 showDownload : true,
32284 getAutoCreate : function()
32288 cls : 'roo-document-viewer',
32292 cls : 'roo-document-viewer-body',
32296 cls : 'roo-document-viewer-thumb',
32300 cls : 'roo-document-viewer-image'
32308 cls : 'roo-document-viewer-footer',
32311 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32315 cls : 'btn-group roo-document-viewer-download',
32319 cls : 'btn btn-default',
32320 html : '<i class="fa fa-download"></i>'
32326 cls : 'btn-group roo-document-viewer-trash',
32330 cls : 'btn btn-default',
32331 html : '<i class="fa fa-trash"></i>'
32344 initEvents : function()
32346 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32347 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32349 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32350 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32352 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32353 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32355 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32356 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32358 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32359 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32361 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32362 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32364 this.bodyEl.on('click', this.onClick, this);
32365 this.downloadBtn.on('click', this.onDownload, this);
32366 this.trashBtn.on('click', this.onTrash, this);
32368 this.downloadBtn.hide();
32369 this.trashBtn.hide();
32371 if(this.showDownload){
32372 this.downloadBtn.show();
32375 if(this.showTrash){
32376 this.trashBtn.show();
32379 if(!this.showDownload && !this.showTrash) {
32380 this.footerEl.hide();
32385 initial : function()
32387 this.fireEvent('initial', this);
32391 onClick : function(e)
32393 e.preventDefault();
32395 this.fireEvent('click', this);
32398 onDownload : function(e)
32400 e.preventDefault();
32402 this.fireEvent('download', this);
32405 onTrash : function(e)
32407 e.preventDefault();
32409 this.fireEvent('trash', this);
32421 * @class Roo.bootstrap.NavProgressBar
32422 * @extends Roo.bootstrap.Component
32423 * Bootstrap NavProgressBar class
32426 * Create a new nav progress bar
32427 * @param {Object} config The config object
32430 Roo.bootstrap.NavProgressBar = function(config){
32431 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32433 this.bullets = this.bullets || [];
32435 // Roo.bootstrap.NavProgressBar.register(this);
32439 * Fires when the active item changes
32440 * @param {Roo.bootstrap.NavProgressBar} this
32441 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32442 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32449 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32454 getAutoCreate : function()
32456 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32460 cls : 'roo-navigation-bar-group',
32464 cls : 'roo-navigation-top-bar'
32468 cls : 'roo-navigation-bullets-bar',
32472 cls : 'roo-navigation-bar'
32479 cls : 'roo-navigation-bottom-bar'
32489 initEvents: function()
32494 onRender : function(ct, position)
32496 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32498 if(this.bullets.length){
32499 Roo.each(this.bullets, function(b){
32508 addItem : function(cfg)
32510 var item = new Roo.bootstrap.NavProgressItem(cfg);
32512 item.parentId = this.id;
32513 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32516 var top = new Roo.bootstrap.Element({
32518 cls : 'roo-navigation-bar-text'
32521 var bottom = new Roo.bootstrap.Element({
32523 cls : 'roo-navigation-bar-text'
32526 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32527 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32529 var topText = new Roo.bootstrap.Element({
32531 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32534 var bottomText = new Roo.bootstrap.Element({
32536 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32539 topText.onRender(top.el, null);
32540 bottomText.onRender(bottom.el, null);
32543 item.bottomEl = bottom;
32546 this.barItems.push(item);
32551 getActive : function()
32553 var active = false;
32555 Roo.each(this.barItems, function(v){
32557 if (!v.isActive()) {
32569 setActiveItem : function(item)
32573 Roo.each(this.barItems, function(v){
32574 if (v.rid == item.rid) {
32578 if (v.isActive()) {
32579 v.setActive(false);
32584 item.setActive(true);
32586 this.fireEvent('changed', this, item, prev);
32589 getBarItem: function(rid)
32593 Roo.each(this.barItems, function(e) {
32594 if (e.rid != rid) {
32605 indexOfItem : function(item)
32609 Roo.each(this.barItems, function(v, i){
32611 if (v.rid != item.rid) {
32622 setActiveNext : function()
32624 var i = this.indexOfItem(this.getActive());
32626 if (i > this.barItems.length) {
32630 this.setActiveItem(this.barItems[i+1]);
32633 setActivePrev : function()
32635 var i = this.indexOfItem(this.getActive());
32641 this.setActiveItem(this.barItems[i-1]);
32644 format : function()
32646 if(!this.barItems.length){
32650 var width = 100 / this.barItems.length;
32652 Roo.each(this.barItems, function(i){
32653 i.el.setStyle('width', width + '%');
32654 i.topEl.el.setStyle('width', width + '%');
32655 i.bottomEl.el.setStyle('width', width + '%');
32664 * Nav Progress Item
32669 * @class Roo.bootstrap.NavProgressItem
32670 * @extends Roo.bootstrap.Component
32671 * Bootstrap NavProgressItem class
32672 * @cfg {String} rid the reference id
32673 * @cfg {Boolean} active (true|false) Is item active default false
32674 * @cfg {Boolean} disabled (true|false) Is item active default false
32675 * @cfg {String} html
32676 * @cfg {String} position (top|bottom) text position default bottom
32677 * @cfg {String} icon show icon instead of number
32680 * Create a new NavProgressItem
32681 * @param {Object} config The config object
32683 Roo.bootstrap.NavProgressItem = function(config){
32684 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32689 * The raw click event for the entire grid.
32690 * @param {Roo.bootstrap.NavProgressItem} this
32691 * @param {Roo.EventObject} e
32698 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32704 position : 'bottom',
32707 getAutoCreate : function()
32709 var iconCls = 'roo-navigation-bar-item-icon';
32711 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32715 cls: 'roo-navigation-bar-item',
32725 cfg.cls += ' active';
32728 cfg.cls += ' disabled';
32734 disable : function()
32736 this.setDisabled(true);
32739 enable : function()
32741 this.setDisabled(false);
32744 initEvents: function()
32746 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32748 this.iconEl.on('click', this.onClick, this);
32751 onClick : function(e)
32753 e.preventDefault();
32759 if(this.fireEvent('click', this, e) === false){
32763 this.parent().setActiveItem(this);
32766 isActive: function ()
32768 return this.active;
32771 setActive : function(state)
32773 if(this.active == state){
32777 this.active = state;
32780 this.el.addClass('active');
32784 this.el.removeClass('active');
32789 setDisabled : function(state)
32791 if(this.disabled == state){
32795 this.disabled = state;
32798 this.el.addClass('disabled');
32802 this.el.removeClass('disabled');
32805 tooltipEl : function()
32807 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32820 * @class Roo.bootstrap.FieldLabel
32821 * @extends Roo.bootstrap.Component
32822 * Bootstrap FieldLabel class
32823 * @cfg {String} html contents of the element
32824 * @cfg {String} tag tag of the element default label
32825 * @cfg {String} cls class of the element
32826 * @cfg {String} target label target
32827 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32828 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32829 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32830 * @cfg {String} iconTooltip default "This field is required"
32831 * @cfg {String} indicatorpos (left|right) default left
32834 * Create a new FieldLabel
32835 * @param {Object} config The config object
32838 Roo.bootstrap.FieldLabel = function(config){
32839 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32844 * Fires after the field has been marked as invalid.
32845 * @param {Roo.form.FieldLabel} this
32846 * @param {String} msg The validation message
32851 * Fires after the field has been validated with no errors.
32852 * @param {Roo.form.FieldLabel} this
32858 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32865 invalidClass : 'has-warning',
32866 validClass : 'has-success',
32867 iconTooltip : 'This field is required',
32868 indicatorpos : 'left',
32870 getAutoCreate : function(){
32873 if (!this.allowBlank) {
32879 cls : 'roo-bootstrap-field-label ' + this.cls,
32884 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32885 tooltip : this.iconTooltip
32894 if(this.indicatorpos == 'right'){
32897 cls : 'roo-bootstrap-field-label ' + this.cls,
32906 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32907 tooltip : this.iconTooltip
32916 initEvents: function()
32918 Roo.bootstrap.Element.superclass.initEvents.call(this);
32920 this.indicator = this.indicatorEl();
32922 if(this.indicator){
32923 this.indicator.removeClass('visible');
32924 this.indicator.addClass('invisible');
32927 Roo.bootstrap.FieldLabel.register(this);
32930 indicatorEl : function()
32932 var indicator = this.el.select('i.roo-required-indicator',true).first();
32943 * Mark this field as valid
32945 markValid : function()
32947 if(this.indicator){
32948 this.indicator.removeClass('visible');
32949 this.indicator.addClass('invisible');
32951 if (Roo.bootstrap.version == 3) {
32952 this.el.removeClass(this.invalidClass);
32953 this.el.addClass(this.validClass);
32955 this.el.removeClass('is-invalid');
32956 this.el.addClass('is-valid');
32960 this.fireEvent('valid', this);
32964 * Mark this field as invalid
32965 * @param {String} msg The validation message
32967 markInvalid : function(msg)
32969 if(this.indicator){
32970 this.indicator.removeClass('invisible');
32971 this.indicator.addClass('visible');
32973 if (Roo.bootstrap.version == 3) {
32974 this.el.removeClass(this.validClass);
32975 this.el.addClass(this.invalidClass);
32977 this.el.removeClass('is-valid');
32978 this.el.addClass('is-invalid');
32982 this.fireEvent('invalid', this, msg);
32988 Roo.apply(Roo.bootstrap.FieldLabel, {
32993 * register a FieldLabel Group
32994 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32996 register : function(label)
32998 if(this.groups.hasOwnProperty(label.target)){
33002 this.groups[label.target] = label;
33006 * fetch a FieldLabel Group based on the target
33007 * @param {string} target
33008 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33010 get: function(target) {
33011 if (typeof(this.groups[target]) == 'undefined') {
33015 return this.groups[target] ;
33024 * page DateSplitField.
33030 * @class Roo.bootstrap.DateSplitField
33031 * @extends Roo.bootstrap.Component
33032 * Bootstrap DateSplitField class
33033 * @cfg {string} fieldLabel - the label associated
33034 * @cfg {Number} labelWidth set the width of label (0-12)
33035 * @cfg {String} labelAlign (top|left)
33036 * @cfg {Boolean} dayAllowBlank (true|false) default false
33037 * @cfg {Boolean} monthAllowBlank (true|false) default false
33038 * @cfg {Boolean} yearAllowBlank (true|false) default false
33039 * @cfg {string} dayPlaceholder
33040 * @cfg {string} monthPlaceholder
33041 * @cfg {string} yearPlaceholder
33042 * @cfg {string} dayFormat default 'd'
33043 * @cfg {string} monthFormat default 'm'
33044 * @cfg {string} yearFormat default 'Y'
33045 * @cfg {Number} labellg set the width of label (1-12)
33046 * @cfg {Number} labelmd set the width of label (1-12)
33047 * @cfg {Number} labelsm set the width of label (1-12)
33048 * @cfg {Number} labelxs set the width of label (1-12)
33052 * Create a new DateSplitField
33053 * @param {Object} config The config object
33056 Roo.bootstrap.DateSplitField = function(config){
33057 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33063 * getting the data of years
33064 * @param {Roo.bootstrap.DateSplitField} this
33065 * @param {Object} years
33070 * getting the data of days
33071 * @param {Roo.bootstrap.DateSplitField} this
33072 * @param {Object} days
33077 * Fires after the field has been marked as invalid.
33078 * @param {Roo.form.Field} this
33079 * @param {String} msg The validation message
33084 * Fires after the field has been validated with no errors.
33085 * @param {Roo.form.Field} this
33091 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33094 labelAlign : 'top',
33096 dayAllowBlank : false,
33097 monthAllowBlank : false,
33098 yearAllowBlank : false,
33099 dayPlaceholder : '',
33100 monthPlaceholder : '',
33101 yearPlaceholder : '',
33105 isFormField : true,
33111 getAutoCreate : function()
33115 cls : 'row roo-date-split-field-group',
33120 cls : 'form-hidden-field roo-date-split-field-group-value',
33126 var labelCls = 'col-md-12';
33127 var contentCls = 'col-md-4';
33129 if(this.fieldLabel){
33133 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33137 html : this.fieldLabel
33142 if(this.labelAlign == 'left'){
33144 if(this.labelWidth > 12){
33145 label.style = "width: " + this.labelWidth + 'px';
33148 if(this.labelWidth < 13 && this.labelmd == 0){
33149 this.labelmd = this.labelWidth;
33152 if(this.labellg > 0){
33153 labelCls = ' col-lg-' + this.labellg;
33154 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33157 if(this.labelmd > 0){
33158 labelCls = ' col-md-' + this.labelmd;
33159 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33162 if(this.labelsm > 0){
33163 labelCls = ' col-sm-' + this.labelsm;
33164 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33167 if(this.labelxs > 0){
33168 labelCls = ' col-xs-' + this.labelxs;
33169 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33173 label.cls += ' ' + labelCls;
33175 cfg.cn.push(label);
33178 Roo.each(['day', 'month', 'year'], function(t){
33181 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33188 inputEl: function ()
33190 return this.el.select('.roo-date-split-field-group-value', true).first();
33193 onRender : function(ct, position)
33197 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33199 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33201 this.dayField = new Roo.bootstrap.ComboBox({
33202 allowBlank : this.dayAllowBlank,
33203 alwaysQuery : true,
33204 displayField : 'value',
33207 forceSelection : true,
33209 placeholder : this.dayPlaceholder,
33210 selectOnFocus : true,
33211 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33212 triggerAction : 'all',
33214 valueField : 'value',
33215 store : new Roo.data.SimpleStore({
33216 data : (function() {
33218 _this.fireEvent('days', _this, days);
33221 fields : [ 'value' ]
33224 select : function (_self, record, index)
33226 _this.setValue(_this.getValue());
33231 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33233 this.monthField = new Roo.bootstrap.MonthField({
33234 after : '<i class=\"fa fa-calendar\"></i>',
33235 allowBlank : this.monthAllowBlank,
33236 placeholder : this.monthPlaceholder,
33239 render : function (_self)
33241 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33242 e.preventDefault();
33246 select : function (_self, oldvalue, newvalue)
33248 _this.setValue(_this.getValue());
33253 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33255 this.yearField = new Roo.bootstrap.ComboBox({
33256 allowBlank : this.yearAllowBlank,
33257 alwaysQuery : true,
33258 displayField : 'value',
33261 forceSelection : true,
33263 placeholder : this.yearPlaceholder,
33264 selectOnFocus : true,
33265 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33266 triggerAction : 'all',
33268 valueField : 'value',
33269 store : new Roo.data.SimpleStore({
33270 data : (function() {
33272 _this.fireEvent('years', _this, years);
33275 fields : [ 'value' ]
33278 select : function (_self, record, index)
33280 _this.setValue(_this.getValue());
33285 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33288 setValue : function(v, format)
33290 this.inputEl.dom.value = v;
33292 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33294 var d = Date.parseDate(v, f);
33301 this.setDay(d.format(this.dayFormat));
33302 this.setMonth(d.format(this.monthFormat));
33303 this.setYear(d.format(this.yearFormat));
33310 setDay : function(v)
33312 this.dayField.setValue(v);
33313 this.inputEl.dom.value = this.getValue();
33318 setMonth : function(v)
33320 this.monthField.setValue(v, true);
33321 this.inputEl.dom.value = this.getValue();
33326 setYear : function(v)
33328 this.yearField.setValue(v);
33329 this.inputEl.dom.value = this.getValue();
33334 getDay : function()
33336 return this.dayField.getValue();
33339 getMonth : function()
33341 return this.monthField.getValue();
33344 getYear : function()
33346 return this.yearField.getValue();
33349 getValue : function()
33351 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33353 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33363 this.inputEl.dom.value = '';
33368 validate : function()
33370 var d = this.dayField.validate();
33371 var m = this.monthField.validate();
33372 var y = this.yearField.validate();
33377 (!this.dayAllowBlank && !d) ||
33378 (!this.monthAllowBlank && !m) ||
33379 (!this.yearAllowBlank && !y)
33384 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33393 this.markInvalid();
33398 markValid : function()
33401 var label = this.el.select('label', true).first();
33402 var icon = this.el.select('i.fa-star', true).first();
33408 this.fireEvent('valid', this);
33412 * Mark this field as invalid
33413 * @param {String} msg The validation message
33415 markInvalid : function(msg)
33418 var label = this.el.select('label', true).first();
33419 var icon = this.el.select('i.fa-star', true).first();
33421 if(label && !icon){
33422 this.el.select('.roo-date-split-field-label', true).createChild({
33424 cls : 'text-danger fa fa-lg fa-star',
33425 tooltip : 'This field is required',
33426 style : 'margin-right:5px;'
33430 this.fireEvent('invalid', this, msg);
33433 clearInvalid : function()
33435 var label = this.el.select('label', true).first();
33436 var icon = this.el.select('i.fa-star', true).first();
33442 this.fireEvent('valid', this);
33445 getName: function()
33455 * http://masonry.desandro.com
33457 * The idea is to render all the bricks based on vertical width...
33459 * The original code extends 'outlayer' - we might need to use that....
33465 * @class Roo.bootstrap.LayoutMasonry
33466 * @extends Roo.bootstrap.Component
33467 * Bootstrap Layout Masonry class
33470 * Create a new Element
33471 * @param {Object} config The config object
33474 Roo.bootstrap.LayoutMasonry = function(config){
33476 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33480 Roo.bootstrap.LayoutMasonry.register(this);
33486 * Fire after layout the items
33487 * @param {Roo.bootstrap.LayoutMasonry} this
33488 * @param {Roo.EventObject} e
33495 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33498 * @cfg {Boolean} isLayoutInstant = no animation?
33500 isLayoutInstant : false, // needed?
33503 * @cfg {Number} boxWidth width of the columns
33508 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33513 * @cfg {Number} padWidth padding below box..
33518 * @cfg {Number} gutter gutter width..
33523 * @cfg {Number} maxCols maximum number of columns
33529 * @cfg {Boolean} isAutoInitial defalut true
33531 isAutoInitial : true,
33536 * @cfg {Boolean} isHorizontal defalut false
33538 isHorizontal : false,
33540 currentSize : null,
33546 bricks: null, //CompositeElement
33550 _isLayoutInited : false,
33552 // isAlternative : false, // only use for vertical layout...
33555 * @cfg {Number} alternativePadWidth padding below box..
33557 alternativePadWidth : 50,
33559 selectedBrick : [],
33561 getAutoCreate : function(){
33563 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33567 cls: 'blog-masonary-wrapper ' + this.cls,
33569 cls : 'mas-boxes masonary'
33576 getChildContainer: function( )
33578 if (this.boxesEl) {
33579 return this.boxesEl;
33582 this.boxesEl = this.el.select('.mas-boxes').first();
33584 return this.boxesEl;
33588 initEvents : function()
33592 if(this.isAutoInitial){
33593 Roo.log('hook children rendered');
33594 this.on('childrenrendered', function() {
33595 Roo.log('children rendered');
33601 initial : function()
33603 this.selectedBrick = [];
33605 this.currentSize = this.el.getBox(true);
33607 Roo.EventManager.onWindowResize(this.resize, this);
33609 if(!this.isAutoInitial){
33617 //this.layout.defer(500,this);
33621 resize : function()
33623 var cs = this.el.getBox(true);
33626 this.currentSize.width == cs.width &&
33627 this.currentSize.x == cs.x &&
33628 this.currentSize.height == cs.height &&
33629 this.currentSize.y == cs.y
33631 Roo.log("no change in with or X or Y");
33635 this.currentSize = cs;
33641 layout : function()
33643 this._resetLayout();
33645 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33647 this.layoutItems( isInstant );
33649 this._isLayoutInited = true;
33651 this.fireEvent('layout', this);
33655 _resetLayout : function()
33657 if(this.isHorizontal){
33658 this.horizontalMeasureColumns();
33662 this.verticalMeasureColumns();
33666 verticalMeasureColumns : function()
33668 this.getContainerWidth();
33670 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33671 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33675 var boxWidth = this.boxWidth + this.padWidth;
33677 if(this.containerWidth < this.boxWidth){
33678 boxWidth = this.containerWidth
33681 var containerWidth = this.containerWidth;
33683 var cols = Math.floor(containerWidth / boxWidth);
33685 this.cols = Math.max( cols, 1 );
33687 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33689 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33691 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33693 this.colWidth = boxWidth + avail - this.padWidth;
33695 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33696 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33699 horizontalMeasureColumns : function()
33701 this.getContainerWidth();
33703 var boxWidth = this.boxWidth;
33705 if(this.containerWidth < boxWidth){
33706 boxWidth = this.containerWidth;
33709 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33711 this.el.setHeight(boxWidth);
33715 getContainerWidth : function()
33717 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33720 layoutItems : function( isInstant )
33722 Roo.log(this.bricks);
33724 var items = Roo.apply([], this.bricks);
33726 if(this.isHorizontal){
33727 this._horizontalLayoutItems( items , isInstant );
33731 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33732 // this._verticalAlternativeLayoutItems( items , isInstant );
33736 this._verticalLayoutItems( items , isInstant );
33740 _verticalLayoutItems : function ( items , isInstant)
33742 if ( !items || !items.length ) {
33747 ['xs', 'xs', 'xs', 'tall'],
33748 ['xs', 'xs', 'tall'],
33749 ['xs', 'xs', 'sm'],
33750 ['xs', 'xs', 'xs'],
33756 ['sm', 'xs', 'xs'],
33760 ['tall', 'xs', 'xs', 'xs'],
33761 ['tall', 'xs', 'xs'],
33773 Roo.each(items, function(item, k){
33775 switch (item.size) {
33776 // these layouts take up a full box,
33787 boxes.push([item]);
33810 var filterPattern = function(box, length)
33818 var pattern = box.slice(0, length);
33822 Roo.each(pattern, function(i){
33823 format.push(i.size);
33826 Roo.each(standard, function(s){
33828 if(String(s) != String(format)){
33837 if(!match && length == 1){
33842 filterPattern(box, length - 1);
33846 queue.push(pattern);
33848 box = box.slice(length, box.length);
33850 filterPattern(box, 4);
33856 Roo.each(boxes, function(box, k){
33862 if(box.length == 1){
33867 filterPattern(box, 4);
33871 this._processVerticalLayoutQueue( queue, isInstant );
33875 // _verticalAlternativeLayoutItems : function( items , isInstant )
33877 // if ( !items || !items.length ) {
33881 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33885 _horizontalLayoutItems : function ( items , isInstant)
33887 if ( !items || !items.length || items.length < 3) {
33893 var eItems = items.slice(0, 3);
33895 items = items.slice(3, items.length);
33898 ['xs', 'xs', 'xs', 'wide'],
33899 ['xs', 'xs', 'wide'],
33900 ['xs', 'xs', 'sm'],
33901 ['xs', 'xs', 'xs'],
33907 ['sm', 'xs', 'xs'],
33911 ['wide', 'xs', 'xs', 'xs'],
33912 ['wide', 'xs', 'xs'],
33925 Roo.each(items, function(item, k){
33927 switch (item.size) {
33938 boxes.push([item]);
33962 var filterPattern = function(box, length)
33970 var pattern = box.slice(0, length);
33974 Roo.each(pattern, function(i){
33975 format.push(i.size);
33978 Roo.each(standard, function(s){
33980 if(String(s) != String(format)){
33989 if(!match && length == 1){
33994 filterPattern(box, length - 1);
33998 queue.push(pattern);
34000 box = box.slice(length, box.length);
34002 filterPattern(box, 4);
34008 Roo.each(boxes, function(box, k){
34014 if(box.length == 1){
34019 filterPattern(box, 4);
34026 var pos = this.el.getBox(true);
34030 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34032 var hit_end = false;
34034 Roo.each(queue, function(box){
34038 Roo.each(box, function(b){
34040 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34050 Roo.each(box, function(b){
34052 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34055 mx = Math.max(mx, b.x);
34059 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34063 Roo.each(box, function(b){
34065 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34079 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34082 /** Sets position of item in DOM
34083 * @param {Element} item
34084 * @param {Number} x - horizontal position
34085 * @param {Number} y - vertical position
34086 * @param {Boolean} isInstant - disables transitions
34088 _processVerticalLayoutQueue : function( queue, isInstant )
34090 var pos = this.el.getBox(true);
34095 for (var i = 0; i < this.cols; i++){
34099 Roo.each(queue, function(box, k){
34101 var col = k % this.cols;
34103 Roo.each(box, function(b,kk){
34105 b.el.position('absolute');
34107 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34108 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34110 if(b.size == 'md-left' || b.size == 'md-right'){
34111 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34112 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34115 b.el.setWidth(width);
34116 b.el.setHeight(height);
34118 b.el.select('iframe',true).setSize(width,height);
34122 for (var i = 0; i < this.cols; i++){
34124 if(maxY[i] < maxY[col]){
34129 col = Math.min(col, i);
34133 x = pos.x + col * (this.colWidth + this.padWidth);
34137 var positions = [];
34139 switch (box.length){
34141 positions = this.getVerticalOneBoxColPositions(x, y, box);
34144 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34147 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34150 positions = this.getVerticalFourBoxColPositions(x, y, box);
34156 Roo.each(box, function(b,kk){
34158 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34160 var sz = b.el.getSize();
34162 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34170 for (var i = 0; i < this.cols; i++){
34171 mY = Math.max(mY, maxY[i]);
34174 this.el.setHeight(mY - pos.y);
34178 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34180 // var pos = this.el.getBox(true);
34183 // var maxX = pos.right;
34185 // var maxHeight = 0;
34187 // Roo.each(items, function(item, k){
34191 // item.el.position('absolute');
34193 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34195 // item.el.setWidth(width);
34197 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34199 // item.el.setHeight(height);
34202 // item.el.setXY([x, y], isInstant ? false : true);
34204 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34207 // y = y + height + this.alternativePadWidth;
34209 // maxHeight = maxHeight + height + this.alternativePadWidth;
34213 // this.el.setHeight(maxHeight);
34217 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34219 var pos = this.el.getBox(true);
34224 var maxX = pos.right;
34226 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34228 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34230 Roo.each(queue, function(box, k){
34232 Roo.each(box, function(b, kk){
34234 b.el.position('absolute');
34236 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34237 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34239 if(b.size == 'md-left' || b.size == 'md-right'){
34240 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34241 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34244 b.el.setWidth(width);
34245 b.el.setHeight(height);
34253 var positions = [];
34255 switch (box.length){
34257 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34260 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34263 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34266 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34272 Roo.each(box, function(b,kk){
34274 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34276 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34284 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34286 Roo.each(eItems, function(b,k){
34288 b.size = (k == 0) ? 'sm' : 'xs';
34289 b.x = (k == 0) ? 2 : 1;
34290 b.y = (k == 0) ? 2 : 1;
34292 b.el.position('absolute');
34294 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34296 b.el.setWidth(width);
34298 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34300 b.el.setHeight(height);
34304 var positions = [];
34307 x : maxX - this.unitWidth * 2 - this.gutter,
34312 x : maxX - this.unitWidth,
34313 y : minY + (this.unitWidth + this.gutter) * 2
34317 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34321 Roo.each(eItems, function(b,k){
34323 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34329 getVerticalOneBoxColPositions : function(x, y, box)
34333 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34335 if(box[0].size == 'md-left'){
34339 if(box[0].size == 'md-right'){
34344 x : x + (this.unitWidth + this.gutter) * rand,
34351 getVerticalTwoBoxColPositions : function(x, y, box)
34355 if(box[0].size == 'xs'){
34359 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34363 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34377 x : x + (this.unitWidth + this.gutter) * 2,
34378 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34385 getVerticalThreeBoxColPositions : function(x, y, box)
34389 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34397 x : x + (this.unitWidth + this.gutter) * 1,
34402 x : x + (this.unitWidth + this.gutter) * 2,
34410 if(box[0].size == 'xs' && box[1].size == 'xs'){
34419 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34423 x : x + (this.unitWidth + this.gutter) * 1,
34437 x : x + (this.unitWidth + this.gutter) * 2,
34442 x : x + (this.unitWidth + this.gutter) * 2,
34443 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34450 getVerticalFourBoxColPositions : function(x, y, box)
34454 if(box[0].size == 'xs'){
34463 y : y + (this.unitHeight + this.gutter) * 1
34468 y : y + (this.unitHeight + this.gutter) * 2
34472 x : x + (this.unitWidth + this.gutter) * 1,
34486 x : x + (this.unitWidth + this.gutter) * 2,
34491 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34492 y : y + (this.unitHeight + this.gutter) * 1
34496 x : x + (this.unitWidth + this.gutter) * 2,
34497 y : y + (this.unitWidth + this.gutter) * 2
34504 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34508 if(box[0].size == 'md-left'){
34510 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34517 if(box[0].size == 'md-right'){
34519 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34520 y : minY + (this.unitWidth + this.gutter) * 1
34526 var rand = Math.floor(Math.random() * (4 - box[0].y));
34529 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34530 y : minY + (this.unitWidth + this.gutter) * rand
34537 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34541 if(box[0].size == 'xs'){
34544 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34549 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34550 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34558 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34563 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34564 y : minY + (this.unitWidth + this.gutter) * 2
34571 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34575 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34578 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34583 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34584 y : minY + (this.unitWidth + this.gutter) * 1
34588 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34589 y : minY + (this.unitWidth + this.gutter) * 2
34596 if(box[0].size == 'xs' && box[1].size == 'xs'){
34599 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34604 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34609 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34610 y : minY + (this.unitWidth + this.gutter) * 1
34618 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34623 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34624 y : minY + (this.unitWidth + this.gutter) * 2
34628 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34629 y : minY + (this.unitWidth + this.gutter) * 2
34636 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34640 if(box[0].size == 'xs'){
34643 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34648 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34653 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),
34658 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34659 y : minY + (this.unitWidth + this.gutter) * 1
34667 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34672 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34673 y : minY + (this.unitWidth + this.gutter) * 2
34677 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34678 y : minY + (this.unitWidth + this.gutter) * 2
34682 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),
34683 y : minY + (this.unitWidth + this.gutter) * 2
34691 * remove a Masonry Brick
34692 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34694 removeBrick : function(brick_id)
34700 for (var i = 0; i<this.bricks.length; i++) {
34701 if (this.bricks[i].id == brick_id) {
34702 this.bricks.splice(i,1);
34703 this.el.dom.removeChild(Roo.get(brick_id).dom);
34710 * adds a Masonry Brick
34711 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34713 addBrick : function(cfg)
34715 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34716 //this.register(cn);
34717 cn.parentId = this.id;
34718 cn.render(this.el);
34723 * register a Masonry Brick
34724 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34727 register : function(brick)
34729 this.bricks.push(brick);
34730 brick.masonryId = this.id;
34734 * clear all the Masonry Brick
34736 clearAll : function()
34739 //this.getChildContainer().dom.innerHTML = "";
34740 this.el.dom.innerHTML = '';
34743 getSelected : function()
34745 if (!this.selectedBrick) {
34749 return this.selectedBrick;
34753 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34757 * register a Masonry Layout
34758 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34761 register : function(layout)
34763 this.groups[layout.id] = layout;
34766 * fetch a Masonry Layout based on the masonry layout ID
34767 * @param {string} the masonry layout to add
34768 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34771 get: function(layout_id) {
34772 if (typeof(this.groups[layout_id]) == 'undefined') {
34775 return this.groups[layout_id] ;
34787 * http://masonry.desandro.com
34789 * The idea is to render all the bricks based on vertical width...
34791 * The original code extends 'outlayer' - we might need to use that....
34797 * @class Roo.bootstrap.LayoutMasonryAuto
34798 * @extends Roo.bootstrap.Component
34799 * Bootstrap Layout Masonry class
34802 * Create a new Element
34803 * @param {Object} config The config object
34806 Roo.bootstrap.LayoutMasonryAuto = function(config){
34807 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34810 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34813 * @cfg {Boolean} isFitWidth - resize the width..
34815 isFitWidth : false, // options..
34817 * @cfg {Boolean} isOriginLeft = left align?
34819 isOriginLeft : true,
34821 * @cfg {Boolean} isOriginTop = top align?
34823 isOriginTop : false,
34825 * @cfg {Boolean} isLayoutInstant = no animation?
34827 isLayoutInstant : false, // needed?
34829 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34831 isResizingContainer : true,
34833 * @cfg {Number} columnWidth width of the columns
34839 * @cfg {Number} maxCols maximum number of columns
34844 * @cfg {Number} padHeight padding below box..
34850 * @cfg {Boolean} isAutoInitial defalut true
34853 isAutoInitial : true,
34859 initialColumnWidth : 0,
34860 currentSize : null,
34862 colYs : null, // array.
34869 bricks: null, //CompositeElement
34870 cols : 0, // array?
34871 // element : null, // wrapped now this.el
34872 _isLayoutInited : null,
34875 getAutoCreate : function(){
34879 cls: 'blog-masonary-wrapper ' + this.cls,
34881 cls : 'mas-boxes masonary'
34888 getChildContainer: function( )
34890 if (this.boxesEl) {
34891 return this.boxesEl;
34894 this.boxesEl = this.el.select('.mas-boxes').first();
34896 return this.boxesEl;
34900 initEvents : function()
34904 if(this.isAutoInitial){
34905 Roo.log('hook children rendered');
34906 this.on('childrenrendered', function() {
34907 Roo.log('children rendered');
34914 initial : function()
34916 this.reloadItems();
34918 this.currentSize = this.el.getBox(true);
34920 /// was window resize... - let's see if this works..
34921 Roo.EventManager.onWindowResize(this.resize, this);
34923 if(!this.isAutoInitial){
34928 this.layout.defer(500,this);
34931 reloadItems: function()
34933 this.bricks = this.el.select('.masonry-brick', true);
34935 this.bricks.each(function(b) {
34936 //Roo.log(b.getSize());
34937 if (!b.attr('originalwidth')) {
34938 b.attr('originalwidth', b.getSize().width);
34943 Roo.log(this.bricks.elements.length);
34946 resize : function()
34949 var cs = this.el.getBox(true);
34951 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34952 Roo.log("no change in with or X");
34955 this.currentSize = cs;
34959 layout : function()
34962 this._resetLayout();
34963 //this._manageStamps();
34965 // don't animate first layout
34966 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34967 this.layoutItems( isInstant );
34969 // flag for initalized
34970 this._isLayoutInited = true;
34973 layoutItems : function( isInstant )
34975 //var items = this._getItemsForLayout( this.items );
34976 // original code supports filtering layout items.. we just ignore it..
34978 this._layoutItems( this.bricks , isInstant );
34980 this._postLayout();
34982 _layoutItems : function ( items , isInstant)
34984 //this.fireEvent( 'layout', this, items );
34987 if ( !items || !items.elements.length ) {
34988 // no items, emit event with empty array
34993 items.each(function(item) {
34994 Roo.log("layout item");
34996 // get x/y object from method
34997 var position = this._getItemLayoutPosition( item );
34999 position.item = item;
35000 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35001 queue.push( position );
35004 this._processLayoutQueue( queue );
35006 /** Sets position of item in DOM
35007 * @param {Element} item
35008 * @param {Number} x - horizontal position
35009 * @param {Number} y - vertical position
35010 * @param {Boolean} isInstant - disables transitions
35012 _processLayoutQueue : function( queue )
35014 for ( var i=0, len = queue.length; i < len; i++ ) {
35015 var obj = queue[i];
35016 obj.item.position('absolute');
35017 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35023 * Any logic you want to do after each layout,
35024 * i.e. size the container
35026 _postLayout : function()
35028 this.resizeContainer();
35031 resizeContainer : function()
35033 if ( !this.isResizingContainer ) {
35036 var size = this._getContainerSize();
35038 this.el.setSize(size.width,size.height);
35039 this.boxesEl.setSize(size.width,size.height);
35045 _resetLayout : function()
35047 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35048 this.colWidth = this.el.getWidth();
35049 //this.gutter = this.el.getWidth();
35051 this.measureColumns();
35057 this.colYs.push( 0 );
35063 measureColumns : function()
35065 this.getContainerWidth();
35066 // if columnWidth is 0, default to outerWidth of first item
35067 if ( !this.columnWidth ) {
35068 var firstItem = this.bricks.first();
35069 Roo.log(firstItem);
35070 this.columnWidth = this.containerWidth;
35071 if (firstItem && firstItem.attr('originalwidth') ) {
35072 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35074 // columnWidth fall back to item of first element
35075 Roo.log("set column width?");
35076 this.initialColumnWidth = this.columnWidth ;
35078 // if first elem has no width, default to size of container
35083 if (this.initialColumnWidth) {
35084 this.columnWidth = this.initialColumnWidth;
35089 // column width is fixed at the top - however if container width get's smaller we should
35092 // this bit calcs how man columns..
35094 var columnWidth = this.columnWidth += this.gutter;
35096 // calculate columns
35097 var containerWidth = this.containerWidth + this.gutter;
35099 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35100 // fix rounding errors, typically with gutters
35101 var excess = columnWidth - containerWidth % columnWidth;
35104 // if overshoot is less than a pixel, round up, otherwise floor it
35105 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35106 cols = Math[ mathMethod ]( cols );
35107 this.cols = Math.max( cols, 1 );
35108 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35110 // padding positioning..
35111 var totalColWidth = this.cols * this.columnWidth;
35112 var padavail = this.containerWidth - totalColWidth;
35113 // so for 2 columns - we need 3 'pads'
35115 var padNeeded = (1+this.cols) * this.padWidth;
35117 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35119 this.columnWidth += padExtra
35120 //this.padWidth = Math.floor(padavail / ( this.cols));
35122 // adjust colum width so that padding is fixed??
35124 // we have 3 columns ... total = width * 3
35125 // we have X left over... that should be used by
35127 //if (this.expandC) {
35135 getContainerWidth : function()
35137 /* // container is parent if fit width
35138 var container = this.isFitWidth ? this.element.parentNode : this.element;
35139 // check that this.size and size are there
35140 // IE8 triggers resize on body size change, so they might not be
35142 var size = getSize( container ); //FIXME
35143 this.containerWidth = size && size.innerWidth; //FIXME
35146 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35150 _getItemLayoutPosition : function( item ) // what is item?
35152 // we resize the item to our columnWidth..
35154 item.setWidth(this.columnWidth);
35155 item.autoBoxAdjust = false;
35157 var sz = item.getSize();
35159 // how many columns does this brick span
35160 var remainder = this.containerWidth % this.columnWidth;
35162 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35163 // round if off by 1 pixel, otherwise use ceil
35164 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35165 colSpan = Math.min( colSpan, this.cols );
35167 // normally this should be '1' as we dont' currently allow multi width columns..
35169 var colGroup = this._getColGroup( colSpan );
35170 // get the minimum Y value from the columns
35171 var minimumY = Math.min.apply( Math, colGroup );
35172 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35174 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35176 // position the brick
35178 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35179 y: this.currentSize.y + minimumY + this.padHeight
35183 // apply setHeight to necessary columns
35184 var setHeight = minimumY + sz.height + this.padHeight;
35185 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35187 var setSpan = this.cols + 1 - colGroup.length;
35188 for ( var i = 0; i < setSpan; i++ ) {
35189 this.colYs[ shortColIndex + i ] = setHeight ;
35196 * @param {Number} colSpan - number of columns the element spans
35197 * @returns {Array} colGroup
35199 _getColGroup : function( colSpan )
35201 if ( colSpan < 2 ) {
35202 // if brick spans only one column, use all the column Ys
35207 // how many different places could this brick fit horizontally
35208 var groupCount = this.cols + 1 - colSpan;
35209 // for each group potential horizontal position
35210 for ( var i = 0; i < groupCount; i++ ) {
35211 // make an array of colY values for that one group
35212 var groupColYs = this.colYs.slice( i, i + colSpan );
35213 // and get the max value of the array
35214 colGroup[i] = Math.max.apply( Math, groupColYs );
35219 _manageStamp : function( stamp )
35221 var stampSize = stamp.getSize();
35222 var offset = stamp.getBox();
35223 // get the columns that this stamp affects
35224 var firstX = this.isOriginLeft ? offset.x : offset.right;
35225 var lastX = firstX + stampSize.width;
35226 var firstCol = Math.floor( firstX / this.columnWidth );
35227 firstCol = Math.max( 0, firstCol );
35229 var lastCol = Math.floor( lastX / this.columnWidth );
35230 // lastCol should not go over if multiple of columnWidth #425
35231 lastCol -= lastX % this.columnWidth ? 0 : 1;
35232 lastCol = Math.min( this.cols - 1, lastCol );
35234 // set colYs to bottom of the stamp
35235 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35238 for ( var i = firstCol; i <= lastCol; i++ ) {
35239 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35244 _getContainerSize : function()
35246 this.maxY = Math.max.apply( Math, this.colYs );
35251 if ( this.isFitWidth ) {
35252 size.width = this._getContainerFitWidth();
35258 _getContainerFitWidth : function()
35260 var unusedCols = 0;
35261 // count unused columns
35264 if ( this.colYs[i] !== 0 ) {
35269 // fit container to columns that have been used
35270 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35273 needsResizeLayout : function()
35275 var previousWidth = this.containerWidth;
35276 this.getContainerWidth();
35277 return previousWidth !== this.containerWidth;
35292 * @class Roo.bootstrap.MasonryBrick
35293 * @extends Roo.bootstrap.Component
35294 * Bootstrap MasonryBrick class
35297 * Create a new MasonryBrick
35298 * @param {Object} config The config object
35301 Roo.bootstrap.MasonryBrick = function(config){
35303 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35305 Roo.bootstrap.MasonryBrick.register(this);
35311 * When a MasonryBrick is clcik
35312 * @param {Roo.bootstrap.MasonryBrick} this
35313 * @param {Roo.EventObject} e
35319 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35322 * @cfg {String} title
35326 * @cfg {String} html
35330 * @cfg {String} bgimage
35334 * @cfg {String} videourl
35338 * @cfg {String} cls
35342 * @cfg {String} href
35346 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35351 * @cfg {String} placetitle (center|bottom)
35356 * @cfg {Boolean} isFitContainer defalut true
35358 isFitContainer : true,
35361 * @cfg {Boolean} preventDefault defalut false
35363 preventDefault : false,
35366 * @cfg {Boolean} inverse defalut false
35368 maskInverse : false,
35370 getAutoCreate : function()
35372 if(!this.isFitContainer){
35373 return this.getSplitAutoCreate();
35376 var cls = 'masonry-brick masonry-brick-full';
35378 if(this.href.length){
35379 cls += ' masonry-brick-link';
35382 if(this.bgimage.length){
35383 cls += ' masonry-brick-image';
35386 if(this.maskInverse){
35387 cls += ' mask-inverse';
35390 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35391 cls += ' enable-mask';
35395 cls += ' masonry-' + this.size + '-brick';
35398 if(this.placetitle.length){
35400 switch (this.placetitle) {
35402 cls += ' masonry-center-title';
35405 cls += ' masonry-bottom-title';
35412 if(!this.html.length && !this.bgimage.length){
35413 cls += ' masonry-center-title';
35416 if(!this.html.length && this.bgimage.length){
35417 cls += ' masonry-bottom-title';
35422 cls += ' ' + this.cls;
35426 tag: (this.href.length) ? 'a' : 'div',
35431 cls: 'masonry-brick-mask'
35435 cls: 'masonry-brick-paragraph',
35441 if(this.href.length){
35442 cfg.href = this.href;
35445 var cn = cfg.cn[1].cn;
35447 if(this.title.length){
35450 cls: 'masonry-brick-title',
35455 if(this.html.length){
35458 cls: 'masonry-brick-text',
35463 if (!this.title.length && !this.html.length) {
35464 cfg.cn[1].cls += ' hide';
35467 if(this.bgimage.length){
35470 cls: 'masonry-brick-image-view',
35475 if(this.videourl.length){
35476 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35477 // youtube support only?
35480 cls: 'masonry-brick-image-view',
35483 allowfullscreen : true
35491 getSplitAutoCreate : function()
35493 var cls = 'masonry-brick masonry-brick-split';
35495 if(this.href.length){
35496 cls += ' masonry-brick-link';
35499 if(this.bgimage.length){
35500 cls += ' masonry-brick-image';
35504 cls += ' masonry-' + this.size + '-brick';
35507 switch (this.placetitle) {
35509 cls += ' masonry-center-title';
35512 cls += ' masonry-bottom-title';
35515 if(!this.bgimage.length){
35516 cls += ' masonry-center-title';
35519 if(this.bgimage.length){
35520 cls += ' masonry-bottom-title';
35526 cls += ' ' + this.cls;
35530 tag: (this.href.length) ? 'a' : 'div',
35535 cls: 'masonry-brick-split-head',
35539 cls: 'masonry-brick-paragraph',
35546 cls: 'masonry-brick-split-body',
35552 if(this.href.length){
35553 cfg.href = this.href;
35556 if(this.title.length){
35557 cfg.cn[0].cn[0].cn.push({
35559 cls: 'masonry-brick-title',
35564 if(this.html.length){
35565 cfg.cn[1].cn.push({
35567 cls: 'masonry-brick-text',
35572 if(this.bgimage.length){
35573 cfg.cn[0].cn.push({
35575 cls: 'masonry-brick-image-view',
35580 if(this.videourl.length){
35581 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35582 // youtube support only?
35583 cfg.cn[0].cn.cn.push({
35585 cls: 'masonry-brick-image-view',
35588 allowfullscreen : true
35595 initEvents: function()
35597 switch (this.size) {
35630 this.el.on('touchstart', this.onTouchStart, this);
35631 this.el.on('touchmove', this.onTouchMove, this);
35632 this.el.on('touchend', this.onTouchEnd, this);
35633 this.el.on('contextmenu', this.onContextMenu, this);
35635 this.el.on('mouseenter' ,this.enter, this);
35636 this.el.on('mouseleave', this.leave, this);
35637 this.el.on('click', this.onClick, this);
35640 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35641 this.parent().bricks.push(this);
35646 onClick: function(e, el)
35648 var time = this.endTimer - this.startTimer;
35649 // Roo.log(e.preventDefault());
35652 e.preventDefault();
35657 if(!this.preventDefault){
35661 e.preventDefault();
35663 if (this.activeClass != '') {
35664 this.selectBrick();
35667 this.fireEvent('click', this, e);
35670 enter: function(e, el)
35672 e.preventDefault();
35674 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35678 if(this.bgimage.length && this.html.length){
35679 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35683 leave: function(e, el)
35685 e.preventDefault();
35687 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35691 if(this.bgimage.length && this.html.length){
35692 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35696 onTouchStart: function(e, el)
35698 // e.preventDefault();
35700 this.touchmoved = false;
35702 if(!this.isFitContainer){
35706 if(!this.bgimage.length || !this.html.length){
35710 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35712 this.timer = new Date().getTime();
35716 onTouchMove: function(e, el)
35718 this.touchmoved = true;
35721 onContextMenu : function(e,el)
35723 e.preventDefault();
35724 e.stopPropagation();
35728 onTouchEnd: function(e, el)
35730 // e.preventDefault();
35732 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35739 if(!this.bgimage.length || !this.html.length){
35741 if(this.href.length){
35742 window.location.href = this.href;
35748 if(!this.isFitContainer){
35752 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35754 window.location.href = this.href;
35757 //selection on single brick only
35758 selectBrick : function() {
35760 if (!this.parentId) {
35764 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35765 var index = m.selectedBrick.indexOf(this.id);
35768 m.selectedBrick.splice(index,1);
35769 this.el.removeClass(this.activeClass);
35773 for(var i = 0; i < m.selectedBrick.length; i++) {
35774 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35775 b.el.removeClass(b.activeClass);
35778 m.selectedBrick = [];
35780 m.selectedBrick.push(this.id);
35781 this.el.addClass(this.activeClass);
35785 isSelected : function(){
35786 return this.el.hasClass(this.activeClass);
35791 Roo.apply(Roo.bootstrap.MasonryBrick, {
35794 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35796 * register a Masonry Brick
35797 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35800 register : function(brick)
35802 //this.groups[brick.id] = brick;
35803 this.groups.add(brick.id, brick);
35806 * fetch a masonry brick based on the masonry brick ID
35807 * @param {string} the masonry brick to add
35808 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35811 get: function(brick_id)
35813 // if (typeof(this.groups[brick_id]) == 'undefined') {
35816 // return this.groups[brick_id] ;
35818 if(this.groups.key(brick_id)) {
35819 return this.groups.key(brick_id);
35837 * @class Roo.bootstrap.Brick
35838 * @extends Roo.bootstrap.Component
35839 * Bootstrap Brick class
35842 * Create a new Brick
35843 * @param {Object} config The config object
35846 Roo.bootstrap.Brick = function(config){
35847 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35853 * When a Brick is click
35854 * @param {Roo.bootstrap.Brick} this
35855 * @param {Roo.EventObject} e
35861 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35864 * @cfg {String} title
35868 * @cfg {String} html
35872 * @cfg {String} bgimage
35876 * @cfg {String} cls
35880 * @cfg {String} href
35884 * @cfg {String} video
35888 * @cfg {Boolean} square
35892 getAutoCreate : function()
35894 var cls = 'roo-brick';
35896 if(this.href.length){
35897 cls += ' roo-brick-link';
35900 if(this.bgimage.length){
35901 cls += ' roo-brick-image';
35904 if(!this.html.length && !this.bgimage.length){
35905 cls += ' roo-brick-center-title';
35908 if(!this.html.length && this.bgimage.length){
35909 cls += ' roo-brick-bottom-title';
35913 cls += ' ' + this.cls;
35917 tag: (this.href.length) ? 'a' : 'div',
35922 cls: 'roo-brick-paragraph',
35928 if(this.href.length){
35929 cfg.href = this.href;
35932 var cn = cfg.cn[0].cn;
35934 if(this.title.length){
35937 cls: 'roo-brick-title',
35942 if(this.html.length){
35945 cls: 'roo-brick-text',
35952 if(this.bgimage.length){
35955 cls: 'roo-brick-image-view',
35963 initEvents: function()
35965 if(this.title.length || this.html.length){
35966 this.el.on('mouseenter' ,this.enter, this);
35967 this.el.on('mouseleave', this.leave, this);
35970 Roo.EventManager.onWindowResize(this.resize, this);
35972 if(this.bgimage.length){
35973 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35974 this.imageEl.on('load', this.onImageLoad, this);
35981 onImageLoad : function()
35986 resize : function()
35988 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35990 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35992 if(this.bgimage.length){
35993 var image = this.el.select('.roo-brick-image-view', true).first();
35995 image.setWidth(paragraph.getWidth());
35998 image.setHeight(paragraph.getWidth());
36001 this.el.setHeight(image.getHeight());
36002 paragraph.setHeight(image.getHeight());
36008 enter: function(e, el)
36010 e.preventDefault();
36012 if(this.bgimage.length){
36013 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36014 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36018 leave: function(e, el)
36020 e.preventDefault();
36022 if(this.bgimage.length){
36023 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36024 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36039 * @class Roo.bootstrap.NumberField
36040 * @extends Roo.bootstrap.Input
36041 * Bootstrap NumberField class
36047 * Create a new NumberField
36048 * @param {Object} config The config object
36051 Roo.bootstrap.NumberField = function(config){
36052 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36055 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36058 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36060 allowDecimals : true,
36062 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36064 decimalSeparator : ".",
36066 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36068 decimalPrecision : 2,
36070 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36072 allowNegative : true,
36075 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36079 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36081 minValue : Number.NEGATIVE_INFINITY,
36083 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36085 maxValue : Number.MAX_VALUE,
36087 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36089 minText : "The minimum value for this field is {0}",
36091 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36093 maxText : "The maximum value for this field is {0}",
36095 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36096 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36098 nanText : "{0} is not a valid number",
36100 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36102 thousandsDelimiter : false,
36104 * @cfg {String} valueAlign alignment of value
36106 valueAlign : "left",
36108 getAutoCreate : function()
36110 var hiddenInput = {
36114 cls: 'hidden-number-input'
36118 hiddenInput.name = this.name;
36123 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36125 this.name = hiddenInput.name;
36127 if(cfg.cn.length > 0) {
36128 cfg.cn.push(hiddenInput);
36135 initEvents : function()
36137 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36139 var allowed = "0123456789";
36141 if(this.allowDecimals){
36142 allowed += this.decimalSeparator;
36145 if(this.allowNegative){
36149 if(this.thousandsDelimiter) {
36153 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36155 var keyPress = function(e){
36157 var k = e.getKey();
36159 var c = e.getCharCode();
36162 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36163 allowed.indexOf(String.fromCharCode(c)) === -1
36169 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36173 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36178 this.el.on("keypress", keyPress, this);
36181 validateValue : function(value)
36184 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36188 var num = this.parseValue(value);
36191 this.markInvalid(String.format(this.nanText, value));
36195 if(num < this.minValue){
36196 this.markInvalid(String.format(this.minText, this.minValue));
36200 if(num > this.maxValue){
36201 this.markInvalid(String.format(this.maxText, this.maxValue));
36208 getValue : function()
36210 var v = this.hiddenEl().getValue();
36212 return this.fixPrecision(this.parseValue(v));
36215 parseValue : function(value)
36217 if(this.thousandsDelimiter) {
36219 r = new RegExp(",", "g");
36220 value = value.replace(r, "");
36223 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36224 return isNaN(value) ? '' : value;
36227 fixPrecision : function(value)
36229 if(this.thousandsDelimiter) {
36231 r = new RegExp(",", "g");
36232 value = value.replace(r, "");
36235 var nan = isNaN(value);
36237 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36238 return nan ? '' : value;
36240 return parseFloat(value).toFixed(this.decimalPrecision);
36243 setValue : function(v)
36245 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36251 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36253 this.inputEl().dom.value = (v == '') ? '' :
36254 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36256 if(!this.allowZero && v === '0') {
36257 this.hiddenEl().dom.value = '';
36258 this.inputEl().dom.value = '';
36265 decimalPrecisionFcn : function(v)
36267 return Math.floor(v);
36270 beforeBlur : function()
36272 var v = this.parseValue(this.getRawValue());
36274 if(v || v === 0 || v === ''){
36279 hiddenEl : function()
36281 return this.el.select('input.hidden-number-input',true).first();
36293 * @class Roo.bootstrap.DocumentSlider
36294 * @extends Roo.bootstrap.Component
36295 * Bootstrap DocumentSlider class
36298 * Create a new DocumentViewer
36299 * @param {Object} config The config object
36302 Roo.bootstrap.DocumentSlider = function(config){
36303 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36310 * Fire after initEvent
36311 * @param {Roo.bootstrap.DocumentSlider} this
36316 * Fire after update
36317 * @param {Roo.bootstrap.DocumentSlider} this
36323 * @param {Roo.bootstrap.DocumentSlider} this
36329 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36335 getAutoCreate : function()
36339 cls : 'roo-document-slider',
36343 cls : 'roo-document-slider-header',
36347 cls : 'roo-document-slider-header-title'
36353 cls : 'roo-document-slider-body',
36357 cls : 'roo-document-slider-prev',
36361 cls : 'fa fa-chevron-left'
36367 cls : 'roo-document-slider-thumb',
36371 cls : 'roo-document-slider-image'
36377 cls : 'roo-document-slider-next',
36381 cls : 'fa fa-chevron-right'
36393 initEvents : function()
36395 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36396 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36398 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36399 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36401 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36402 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36404 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36405 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36407 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36408 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36410 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36411 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36413 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36414 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36416 this.thumbEl.on('click', this.onClick, this);
36418 this.prevIndicator.on('click', this.prev, this);
36420 this.nextIndicator.on('click', this.next, this);
36424 initial : function()
36426 if(this.files.length){
36427 this.indicator = 1;
36431 this.fireEvent('initial', this);
36434 update : function()
36436 this.imageEl.attr('src', this.files[this.indicator - 1]);
36438 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36440 this.prevIndicator.show();
36442 if(this.indicator == 1){
36443 this.prevIndicator.hide();
36446 this.nextIndicator.show();
36448 if(this.indicator == this.files.length){
36449 this.nextIndicator.hide();
36452 this.thumbEl.scrollTo('top');
36454 this.fireEvent('update', this);
36457 onClick : function(e)
36459 e.preventDefault();
36461 this.fireEvent('click', this);
36466 e.preventDefault();
36468 this.indicator = Math.max(1, this.indicator - 1);
36475 e.preventDefault();
36477 this.indicator = Math.min(this.files.length, this.indicator + 1);
36491 * @class Roo.bootstrap.RadioSet
36492 * @extends Roo.bootstrap.Input
36493 * Bootstrap RadioSet class
36494 * @cfg {String} indicatorpos (left|right) default left
36495 * @cfg {Boolean} inline (true|false) inline the element (default true)
36496 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36498 * Create a new RadioSet
36499 * @param {Object} config The config object
36502 Roo.bootstrap.RadioSet = function(config){
36504 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36508 Roo.bootstrap.RadioSet.register(this);
36513 * Fires when the element is checked or unchecked.
36514 * @param {Roo.bootstrap.RadioSet} this This radio
36515 * @param {Roo.bootstrap.Radio} item The checked item
36520 * Fires when the element is click.
36521 * @param {Roo.bootstrap.RadioSet} this This radio set
36522 * @param {Roo.bootstrap.Radio} item The checked item
36523 * @param {Roo.EventObject} e The event object
36530 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36538 indicatorpos : 'left',
36540 getAutoCreate : function()
36544 cls : 'roo-radio-set-label',
36548 html : this.fieldLabel
36552 if (Roo.bootstrap.version == 3) {
36555 if(this.indicatorpos == 'left'){
36558 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36559 tooltip : 'This field is required'
36564 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36565 tooltip : 'This field is required'
36571 cls : 'roo-radio-set-items'
36574 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36576 if (align === 'left' && this.fieldLabel.length) {
36579 cls : "roo-radio-set-right",
36585 if(this.labelWidth > 12){
36586 label.style = "width: " + this.labelWidth + 'px';
36589 if(this.labelWidth < 13 && this.labelmd == 0){
36590 this.labelmd = this.labelWidth;
36593 if(this.labellg > 0){
36594 label.cls += ' col-lg-' + this.labellg;
36595 items.cls += ' col-lg-' + (12 - this.labellg);
36598 if(this.labelmd > 0){
36599 label.cls += ' col-md-' + this.labelmd;
36600 items.cls += ' col-md-' + (12 - this.labelmd);
36603 if(this.labelsm > 0){
36604 label.cls += ' col-sm-' + this.labelsm;
36605 items.cls += ' col-sm-' + (12 - this.labelsm);
36608 if(this.labelxs > 0){
36609 label.cls += ' col-xs-' + this.labelxs;
36610 items.cls += ' col-xs-' + (12 - this.labelxs);
36616 cls : 'roo-radio-set',
36620 cls : 'roo-radio-set-input',
36623 value : this.value ? this.value : ''
36630 if(this.weight.length){
36631 cfg.cls += ' roo-radio-' + this.weight;
36635 cfg.cls += ' roo-radio-set-inline';
36639 ['xs','sm','md','lg'].map(function(size){
36640 if (settings[size]) {
36641 cfg.cls += ' col-' + size + '-' + settings[size];
36649 initEvents : function()
36651 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36652 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36654 if(!this.fieldLabel.length){
36655 this.labelEl.hide();
36658 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36659 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36661 this.indicator = this.indicatorEl();
36663 if(this.indicator){
36664 this.indicator.addClass('invisible');
36667 this.originalValue = this.getValue();
36671 inputEl: function ()
36673 return this.el.select('.roo-radio-set-input', true).first();
36676 getChildContainer : function()
36678 return this.itemsEl;
36681 register : function(item)
36683 this.radioes.push(item);
36687 validate : function()
36689 if(this.getVisibilityEl().hasClass('hidden')){
36695 Roo.each(this.radioes, function(i){
36704 if(this.allowBlank) {
36708 if(this.disabled || valid){
36713 this.markInvalid();
36718 markValid : function()
36720 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36721 this.indicatorEl().removeClass('visible');
36722 this.indicatorEl().addClass('invisible');
36726 if (Roo.bootstrap.version == 3) {
36727 this.el.removeClass([this.invalidClass, this.validClass]);
36728 this.el.addClass(this.validClass);
36730 this.el.removeClass(['is-invalid','is-valid']);
36731 this.el.addClass(['is-valid']);
36733 this.fireEvent('valid', this);
36736 markInvalid : function(msg)
36738 if(this.allowBlank || this.disabled){
36742 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36743 this.indicatorEl().removeClass('invisible');
36744 this.indicatorEl().addClass('visible');
36746 if (Roo.bootstrap.version == 3) {
36747 this.el.removeClass([this.invalidClass, this.validClass]);
36748 this.el.addClass(this.invalidClass);
36750 this.el.removeClass(['is-invalid','is-valid']);
36751 this.el.addClass(['is-invalid']);
36754 this.fireEvent('invalid', this, msg);
36758 setValue : function(v, suppressEvent)
36760 if(this.value === v){
36767 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36770 Roo.each(this.radioes, function(i){
36772 i.el.removeClass('checked');
36775 Roo.each(this.radioes, function(i){
36777 if(i.value === v || i.value.toString() === v.toString()){
36779 i.el.addClass('checked');
36781 if(suppressEvent !== true){
36782 this.fireEvent('check', this, i);
36793 clearInvalid : function(){
36795 if(!this.el || this.preventMark){
36799 this.el.removeClass([this.invalidClass]);
36801 this.fireEvent('valid', this);
36806 Roo.apply(Roo.bootstrap.RadioSet, {
36810 register : function(set)
36812 this.groups[set.name] = set;
36815 get: function(name)
36817 if (typeof(this.groups[name]) == 'undefined') {
36821 return this.groups[name] ;
36827 * Ext JS Library 1.1.1
36828 * Copyright(c) 2006-2007, Ext JS, LLC.
36830 * Originally Released Under LGPL - original licence link has changed is not relivant.
36833 * <script type="text/javascript">
36838 * @class Roo.bootstrap.SplitBar
36839 * @extends Roo.util.Observable
36840 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36844 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36845 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36846 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36847 split.minSize = 100;
36848 split.maxSize = 600;
36849 split.animate = true;
36850 split.on('moved', splitterMoved);
36853 * Create a new SplitBar
36854 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36855 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36856 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36857 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36858 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36859 position of the SplitBar).
36861 Roo.bootstrap.SplitBar = function(cfg){
36866 // dragElement : elm
36867 // resizingElement: el,
36869 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36870 // placement : Roo.bootstrap.SplitBar.LEFT ,
36871 // existingProxy ???
36874 this.el = Roo.get(cfg.dragElement, true);
36875 this.el.dom.unselectable = "on";
36877 this.resizingEl = Roo.get(cfg.resizingElement, true);
36881 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36882 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36885 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36888 * The minimum size of the resizing element. (Defaults to 0)
36894 * The maximum size of the resizing element. (Defaults to 2000)
36897 this.maxSize = 2000;
36900 * Whether to animate the transition to the new size
36903 this.animate = false;
36906 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36909 this.useShim = false;
36914 if(!cfg.existingProxy){
36916 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36918 this.proxy = Roo.get(cfg.existingProxy).dom;
36921 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36924 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36927 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36930 this.dragSpecs = {};
36933 * @private The adapter to use to positon and resize elements
36935 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36936 this.adapter.init(this);
36938 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36940 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36941 this.el.addClass("roo-splitbar-h");
36944 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36945 this.el.addClass("roo-splitbar-v");
36951 * Fires when the splitter is moved (alias for {@link #event-moved})
36952 * @param {Roo.bootstrap.SplitBar} this
36953 * @param {Number} newSize the new width or height
36958 * Fires when the splitter is moved
36959 * @param {Roo.bootstrap.SplitBar} this
36960 * @param {Number} newSize the new width or height
36964 * @event beforeresize
36965 * Fires before the splitter is dragged
36966 * @param {Roo.bootstrap.SplitBar} this
36968 "beforeresize" : true,
36970 "beforeapply" : true
36973 Roo.util.Observable.call(this);
36976 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36977 onStartProxyDrag : function(x, y){
36978 this.fireEvent("beforeresize", this);
36980 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36982 o.enableDisplayMode("block");
36983 // all splitbars share the same overlay
36984 Roo.bootstrap.SplitBar.prototype.overlay = o;
36986 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36987 this.overlay.show();
36988 Roo.get(this.proxy).setDisplayed("block");
36989 var size = this.adapter.getElementSize(this);
36990 this.activeMinSize = this.getMinimumSize();;
36991 this.activeMaxSize = this.getMaximumSize();;
36992 var c1 = size - this.activeMinSize;
36993 var c2 = Math.max(this.activeMaxSize - size, 0);
36994 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36995 this.dd.resetConstraints();
36996 this.dd.setXConstraint(
36997 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36998 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37000 this.dd.setYConstraint(0, 0);
37002 this.dd.resetConstraints();
37003 this.dd.setXConstraint(0, 0);
37004 this.dd.setYConstraint(
37005 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37006 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37009 this.dragSpecs.startSize = size;
37010 this.dragSpecs.startPoint = [x, y];
37011 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37015 * @private Called after the drag operation by the DDProxy
37017 onEndProxyDrag : function(e){
37018 Roo.get(this.proxy).setDisplayed(false);
37019 var endPoint = Roo.lib.Event.getXY(e);
37021 this.overlay.hide();
37024 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37025 newSize = this.dragSpecs.startSize +
37026 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37027 endPoint[0] - this.dragSpecs.startPoint[0] :
37028 this.dragSpecs.startPoint[0] - endPoint[0]
37031 newSize = this.dragSpecs.startSize +
37032 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37033 endPoint[1] - this.dragSpecs.startPoint[1] :
37034 this.dragSpecs.startPoint[1] - endPoint[1]
37037 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37038 if(newSize != this.dragSpecs.startSize){
37039 if(this.fireEvent('beforeapply', this, newSize) !== false){
37040 this.adapter.setElementSize(this, newSize);
37041 this.fireEvent("moved", this, newSize);
37042 this.fireEvent("resize", this, newSize);
37048 * Get the adapter this SplitBar uses
37049 * @return The adapter object
37051 getAdapter : function(){
37052 return this.adapter;
37056 * Set the adapter this SplitBar uses
37057 * @param {Object} adapter A SplitBar adapter object
37059 setAdapter : function(adapter){
37060 this.adapter = adapter;
37061 this.adapter.init(this);
37065 * Gets the minimum size for the resizing element
37066 * @return {Number} The minimum size
37068 getMinimumSize : function(){
37069 return this.minSize;
37073 * Sets the minimum size for the resizing element
37074 * @param {Number} minSize The minimum size
37076 setMinimumSize : function(minSize){
37077 this.minSize = minSize;
37081 * Gets the maximum size for the resizing element
37082 * @return {Number} The maximum size
37084 getMaximumSize : function(){
37085 return this.maxSize;
37089 * Sets the maximum size for the resizing element
37090 * @param {Number} maxSize The maximum size
37092 setMaximumSize : function(maxSize){
37093 this.maxSize = maxSize;
37097 * Sets the initialize size for the resizing element
37098 * @param {Number} size The initial size
37100 setCurrentSize : function(size){
37101 var oldAnimate = this.animate;
37102 this.animate = false;
37103 this.adapter.setElementSize(this, size);
37104 this.animate = oldAnimate;
37108 * Destroy this splitbar.
37109 * @param {Boolean} removeEl True to remove the element
37111 destroy : function(removeEl){
37113 this.shim.remove();
37116 this.proxy.parentNode.removeChild(this.proxy);
37124 * @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.
37126 Roo.bootstrap.SplitBar.createProxy = function(dir){
37127 var proxy = new Roo.Element(document.createElement("div"));
37128 proxy.unselectable();
37129 var cls = 'roo-splitbar-proxy';
37130 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37131 document.body.appendChild(proxy.dom);
37136 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37137 * Default Adapter. It assumes the splitter and resizing element are not positioned
37138 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37140 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37143 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37144 // do nothing for now
37145 init : function(s){
37149 * Called before drag operations to get the current size of the resizing element.
37150 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37152 getElementSize : function(s){
37153 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37154 return s.resizingEl.getWidth();
37156 return s.resizingEl.getHeight();
37161 * Called after drag operations to set the size of the resizing element.
37162 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37163 * @param {Number} newSize The new size to set
37164 * @param {Function} onComplete A function to be invoked when resizing is complete
37166 setElementSize : function(s, newSize, onComplete){
37167 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37169 s.resizingEl.setWidth(newSize);
37171 onComplete(s, newSize);
37174 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37179 s.resizingEl.setHeight(newSize);
37181 onComplete(s, newSize);
37184 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37191 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37192 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37193 * Adapter that moves the splitter element to align with the resized sizing element.
37194 * Used with an absolute positioned SplitBar.
37195 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37196 * document.body, make sure you assign an id to the body element.
37198 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37199 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37200 this.container = Roo.get(container);
37203 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37204 init : function(s){
37205 this.basic.init(s);
37208 getElementSize : function(s){
37209 return this.basic.getElementSize(s);
37212 setElementSize : function(s, newSize, onComplete){
37213 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37216 moveSplitter : function(s){
37217 var yes = Roo.bootstrap.SplitBar;
37218 switch(s.placement){
37220 s.el.setX(s.resizingEl.getRight());
37223 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37226 s.el.setY(s.resizingEl.getBottom());
37229 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37236 * Orientation constant - Create a vertical SplitBar
37240 Roo.bootstrap.SplitBar.VERTICAL = 1;
37243 * Orientation constant - Create a horizontal SplitBar
37247 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37250 * Placement constant - The resizing element is to the left of the splitter element
37254 Roo.bootstrap.SplitBar.LEFT = 1;
37257 * Placement constant - The resizing element is to the right of the splitter element
37261 Roo.bootstrap.SplitBar.RIGHT = 2;
37264 * Placement constant - The resizing element is positioned above the splitter element
37268 Roo.bootstrap.SplitBar.TOP = 3;
37271 * Placement constant - The resizing element is positioned under splitter element
37275 Roo.bootstrap.SplitBar.BOTTOM = 4;
37276 Roo.namespace("Roo.bootstrap.layout");/*
37278 * Ext JS Library 1.1.1
37279 * Copyright(c) 2006-2007, Ext JS, LLC.
37281 * Originally Released Under LGPL - original licence link has changed is not relivant.
37284 * <script type="text/javascript">
37288 * @class Roo.bootstrap.layout.Manager
37289 * @extends Roo.bootstrap.Component
37290 * Base class for layout managers.
37292 Roo.bootstrap.layout.Manager = function(config)
37294 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37300 /** false to disable window resize monitoring @type Boolean */
37301 this.monitorWindowResize = true;
37306 * Fires when a layout is performed.
37307 * @param {Roo.LayoutManager} this
37311 * @event regionresized
37312 * Fires when the user resizes a region.
37313 * @param {Roo.LayoutRegion} region The resized region
37314 * @param {Number} newSize The new size (width for east/west, height for north/south)
37316 "regionresized" : true,
37318 * @event regioncollapsed
37319 * Fires when a region is collapsed.
37320 * @param {Roo.LayoutRegion} region The collapsed region
37322 "regioncollapsed" : true,
37324 * @event regionexpanded
37325 * Fires when a region is expanded.
37326 * @param {Roo.LayoutRegion} region The expanded region
37328 "regionexpanded" : true
37330 this.updating = false;
37333 this.el = Roo.get(config.el);
37339 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37344 monitorWindowResize : true,
37350 onRender : function(ct, position)
37353 this.el = Roo.get(ct);
37356 //this.fireEvent('render',this);
37360 initEvents: function()
37364 // ie scrollbar fix
37365 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37366 document.body.scroll = "no";
37367 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37368 this.el.position('relative');
37370 this.id = this.el.id;
37371 this.el.addClass("roo-layout-container");
37372 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37373 if(this.el.dom != document.body ) {
37374 this.el.on('resize', this.layout,this);
37375 this.el.on('show', this.layout,this);
37381 * Returns true if this layout is currently being updated
37382 * @return {Boolean}
37384 isUpdating : function(){
37385 return this.updating;
37389 * Suspend the LayoutManager from doing auto-layouts while
37390 * making multiple add or remove calls
37392 beginUpdate : function(){
37393 this.updating = true;
37397 * Restore auto-layouts and optionally disable the manager from performing a layout
37398 * @param {Boolean} noLayout true to disable a layout update
37400 endUpdate : function(noLayout){
37401 this.updating = false;
37407 layout: function(){
37411 onRegionResized : function(region, newSize){
37412 this.fireEvent("regionresized", region, newSize);
37416 onRegionCollapsed : function(region){
37417 this.fireEvent("regioncollapsed", region);
37420 onRegionExpanded : function(region){
37421 this.fireEvent("regionexpanded", region);
37425 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37426 * performs box-model adjustments.
37427 * @return {Object} The size as an object {width: (the width), height: (the height)}
37429 getViewSize : function()
37432 if(this.el.dom != document.body){
37433 size = this.el.getSize();
37435 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37437 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37438 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37443 * Returns the Element this layout is bound to.
37444 * @return {Roo.Element}
37446 getEl : function(){
37451 * Returns the specified region.
37452 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37453 * @return {Roo.LayoutRegion}
37455 getRegion : function(target){
37456 return this.regions[target.toLowerCase()];
37459 onWindowResize : function(){
37460 if(this.monitorWindowResize){
37467 * Ext JS Library 1.1.1
37468 * Copyright(c) 2006-2007, Ext JS, LLC.
37470 * Originally Released Under LGPL - original licence link has changed is not relivant.
37473 * <script type="text/javascript">
37476 * @class Roo.bootstrap.layout.Border
37477 * @extends Roo.bootstrap.layout.Manager
37478 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37479 * please see: examples/bootstrap/nested.html<br><br>
37481 <b>The container the layout is rendered into can be either the body element or any other element.
37482 If it is not the body element, the container needs to either be an absolute positioned element,
37483 or you will need to add "position:relative" to the css of the container. You will also need to specify
37484 the container size if it is not the body element.</b>
37487 * Create a new Border
37488 * @param {Object} config Configuration options
37490 Roo.bootstrap.layout.Border = function(config){
37491 config = config || {};
37492 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37496 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37497 if(config[region]){
37498 config[region].region = region;
37499 this.addRegion(config[region]);
37505 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37507 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37509 parent : false, // this might point to a 'nest' or a ???
37512 * Creates and adds a new region if it doesn't already exist.
37513 * @param {String} target The target region key (north, south, east, west or center).
37514 * @param {Object} config The regions config object
37515 * @return {BorderLayoutRegion} The new region
37517 addRegion : function(config)
37519 if(!this.regions[config.region]){
37520 var r = this.factory(config);
37521 this.bindRegion(r);
37523 return this.regions[config.region];
37527 bindRegion : function(r){
37528 this.regions[r.config.region] = r;
37530 r.on("visibilitychange", this.layout, this);
37531 r.on("paneladded", this.layout, this);
37532 r.on("panelremoved", this.layout, this);
37533 r.on("invalidated", this.layout, this);
37534 r.on("resized", this.onRegionResized, this);
37535 r.on("collapsed", this.onRegionCollapsed, this);
37536 r.on("expanded", this.onRegionExpanded, this);
37540 * Performs a layout update.
37542 layout : function()
37544 if(this.updating) {
37548 // render all the rebions if they have not been done alreayd?
37549 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37550 if(this.regions[region] && !this.regions[region].bodyEl){
37551 this.regions[region].onRender(this.el)
37555 var size = this.getViewSize();
37556 var w = size.width;
37557 var h = size.height;
37562 //var x = 0, y = 0;
37564 var rs = this.regions;
37565 var north = rs["north"];
37566 var south = rs["south"];
37567 var west = rs["west"];
37568 var east = rs["east"];
37569 var center = rs["center"];
37570 //if(this.hideOnLayout){ // not supported anymore
37571 //c.el.setStyle("display", "none");
37573 if(north && north.isVisible()){
37574 var b = north.getBox();
37575 var m = north.getMargins();
37576 b.width = w - (m.left+m.right);
37579 centerY = b.height + b.y + m.bottom;
37580 centerH -= centerY;
37581 north.updateBox(this.safeBox(b));
37583 if(south && south.isVisible()){
37584 var b = south.getBox();
37585 var m = south.getMargins();
37586 b.width = w - (m.left+m.right);
37588 var totalHeight = (b.height + m.top + m.bottom);
37589 b.y = h - totalHeight + m.top;
37590 centerH -= totalHeight;
37591 south.updateBox(this.safeBox(b));
37593 if(west && west.isVisible()){
37594 var b = west.getBox();
37595 var m = west.getMargins();
37596 b.height = centerH - (m.top+m.bottom);
37598 b.y = centerY + m.top;
37599 var totalWidth = (b.width + m.left + m.right);
37600 centerX += totalWidth;
37601 centerW -= totalWidth;
37602 west.updateBox(this.safeBox(b));
37604 if(east && east.isVisible()){
37605 var b = east.getBox();
37606 var m = east.getMargins();
37607 b.height = centerH - (m.top+m.bottom);
37608 var totalWidth = (b.width + m.left + m.right);
37609 b.x = w - totalWidth + m.left;
37610 b.y = centerY + m.top;
37611 centerW -= totalWidth;
37612 east.updateBox(this.safeBox(b));
37615 var m = center.getMargins();
37617 x: centerX + m.left,
37618 y: centerY + m.top,
37619 width: centerW - (m.left+m.right),
37620 height: centerH - (m.top+m.bottom)
37622 //if(this.hideOnLayout){
37623 //center.el.setStyle("display", "block");
37625 center.updateBox(this.safeBox(centerBox));
37628 this.fireEvent("layout", this);
37632 safeBox : function(box){
37633 box.width = Math.max(0, box.width);
37634 box.height = Math.max(0, box.height);
37639 * Adds a ContentPanel (or subclass) to this layout.
37640 * @param {String} target The target region key (north, south, east, west or center).
37641 * @param {Roo.ContentPanel} panel The panel to add
37642 * @return {Roo.ContentPanel} The added panel
37644 add : function(target, panel){
37646 target = target.toLowerCase();
37647 return this.regions[target].add(panel);
37651 * Remove a ContentPanel (or subclass) to this layout.
37652 * @param {String} target The target region key (north, south, east, west or center).
37653 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37654 * @return {Roo.ContentPanel} The removed panel
37656 remove : function(target, panel){
37657 target = target.toLowerCase();
37658 return this.regions[target].remove(panel);
37662 * Searches all regions for a panel with the specified id
37663 * @param {String} panelId
37664 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37666 findPanel : function(panelId){
37667 var rs = this.regions;
37668 for(var target in rs){
37669 if(typeof rs[target] != "function"){
37670 var p = rs[target].getPanel(panelId);
37680 * Searches all regions for a panel with the specified id and activates (shows) it.
37681 * @param {String/ContentPanel} panelId The panels id or the panel itself
37682 * @return {Roo.ContentPanel} The shown panel or null
37684 showPanel : function(panelId) {
37685 var rs = this.regions;
37686 for(var target in rs){
37687 var r = rs[target];
37688 if(typeof r != "function"){
37689 if(r.hasPanel(panelId)){
37690 return r.showPanel(panelId);
37698 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37699 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37702 restoreState : function(provider){
37704 provider = Roo.state.Manager;
37706 var sm = new Roo.LayoutStateManager();
37707 sm.init(this, provider);
37713 * Adds a xtype elements to the layout.
37717 xtype : 'ContentPanel',
37724 xtype : 'NestedLayoutPanel',
37730 items : [ ... list of content panels or nested layout panels.. ]
37734 * @param {Object} cfg Xtype definition of item to add.
37736 addxtype : function(cfg)
37738 // basically accepts a pannel...
37739 // can accept a layout region..!?!?
37740 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37743 // theory? children can only be panels??
37745 //if (!cfg.xtype.match(/Panel$/)) {
37750 if (typeof(cfg.region) == 'undefined') {
37751 Roo.log("Failed to add Panel, region was not set");
37755 var region = cfg.region;
37761 xitems = cfg.items;
37766 if ( region == 'center') {
37767 Roo.log("Center: " + cfg.title);
37773 case 'Content': // ContentPanel (el, cfg)
37774 case 'Scroll': // ContentPanel (el, cfg)
37776 cfg.autoCreate = cfg.autoCreate || true;
37777 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37779 // var el = this.el.createChild();
37780 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37783 this.add(region, ret);
37787 case 'TreePanel': // our new panel!
37788 cfg.el = this.el.createChild();
37789 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37790 this.add(region, ret);
37795 // create a new Layout (which is a Border Layout...
37797 var clayout = cfg.layout;
37798 clayout.el = this.el.createChild();
37799 clayout.items = clayout.items || [];
37803 // replace this exitems with the clayout ones..
37804 xitems = clayout.items;
37806 // force background off if it's in center...
37807 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37808 cfg.background = false;
37810 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37813 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37814 //console.log('adding nested layout panel ' + cfg.toSource());
37815 this.add(region, ret);
37816 nb = {}; /// find first...
37821 // needs grid and region
37823 //var el = this.getRegion(region).el.createChild();
37825 *var el = this.el.createChild();
37826 // create the grid first...
37827 cfg.grid.container = el;
37828 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37831 if (region == 'center' && this.active ) {
37832 cfg.background = false;
37835 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37837 this.add(region, ret);
37839 if (cfg.background) {
37840 // render grid on panel activation (if panel background)
37841 ret.on('activate', function(gp) {
37842 if (!gp.grid.rendered) {
37843 // gp.grid.render(el);
37847 // cfg.grid.render(el);
37853 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37854 // it was the old xcomponent building that caused this before.
37855 // espeically if border is the top element in the tree.
37865 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37867 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37868 this.add(region, ret);
37872 throw "Can not add '" + cfg.xtype + "' to Border";
37878 this.beginUpdate();
37882 Roo.each(xitems, function(i) {
37883 region = nb && i.region ? i.region : false;
37885 var add = ret.addxtype(i);
37888 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37889 if (!i.background) {
37890 abn[region] = nb[region] ;
37897 // make the last non-background panel active..
37898 //if (nb) { Roo.log(abn); }
37901 for(var r in abn) {
37902 region = this.getRegion(r);
37904 // tried using nb[r], but it does not work..
37906 region.showPanel(abn[r]);
37917 factory : function(cfg)
37920 var validRegions = Roo.bootstrap.layout.Border.regions;
37922 var target = cfg.region;
37925 var r = Roo.bootstrap.layout;
37929 return new r.North(cfg);
37931 return new r.South(cfg);
37933 return new r.East(cfg);
37935 return new r.West(cfg);
37937 return new r.Center(cfg);
37939 throw 'Layout region "'+target+'" not supported.';
37946 * Ext JS Library 1.1.1
37947 * Copyright(c) 2006-2007, Ext JS, LLC.
37949 * Originally Released Under LGPL - original licence link has changed is not relivant.
37952 * <script type="text/javascript">
37956 * @class Roo.bootstrap.layout.Basic
37957 * @extends Roo.util.Observable
37958 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37959 * and does not have a titlebar, tabs or any other features. All it does is size and position
37960 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37961 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37962 * @cfg {string} region the region that it inhabits..
37963 * @cfg {bool} skipConfig skip config?
37967 Roo.bootstrap.layout.Basic = function(config){
37969 this.mgr = config.mgr;
37971 this.position = config.region;
37973 var skipConfig = config.skipConfig;
37977 * @scope Roo.BasicLayoutRegion
37981 * @event beforeremove
37982 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37983 * @param {Roo.LayoutRegion} this
37984 * @param {Roo.ContentPanel} panel The panel
37985 * @param {Object} e The cancel event object
37987 "beforeremove" : true,
37989 * @event invalidated
37990 * Fires when the layout for this region is changed.
37991 * @param {Roo.LayoutRegion} this
37993 "invalidated" : true,
37995 * @event visibilitychange
37996 * Fires when this region is shown or hidden
37997 * @param {Roo.LayoutRegion} this
37998 * @param {Boolean} visibility true or false
38000 "visibilitychange" : true,
38002 * @event paneladded
38003 * Fires when a panel is added.
38004 * @param {Roo.LayoutRegion} this
38005 * @param {Roo.ContentPanel} panel The panel
38007 "paneladded" : true,
38009 * @event panelremoved
38010 * Fires when a panel is removed.
38011 * @param {Roo.LayoutRegion} this
38012 * @param {Roo.ContentPanel} panel The panel
38014 "panelremoved" : true,
38016 * @event beforecollapse
38017 * Fires when this region before collapse.
38018 * @param {Roo.LayoutRegion} this
38020 "beforecollapse" : true,
38023 * Fires when this region is collapsed.
38024 * @param {Roo.LayoutRegion} this
38026 "collapsed" : true,
38029 * Fires when this region is expanded.
38030 * @param {Roo.LayoutRegion} this
38035 * Fires when this region is slid into view.
38036 * @param {Roo.LayoutRegion} this
38038 "slideshow" : true,
38041 * Fires when this region slides out of view.
38042 * @param {Roo.LayoutRegion} this
38044 "slidehide" : true,
38046 * @event panelactivated
38047 * Fires when a panel is activated.
38048 * @param {Roo.LayoutRegion} this
38049 * @param {Roo.ContentPanel} panel The activated panel
38051 "panelactivated" : true,
38054 * Fires when the user resizes this region.
38055 * @param {Roo.LayoutRegion} this
38056 * @param {Number} newSize The new size (width for east/west, height for north/south)
38060 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38061 this.panels = new Roo.util.MixedCollection();
38062 this.panels.getKey = this.getPanelId.createDelegate(this);
38064 this.activePanel = null;
38065 // ensure listeners are added...
38067 if (config.listeners || config.events) {
38068 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38069 listeners : config.listeners || {},
38070 events : config.events || {}
38074 if(skipConfig !== true){
38075 this.applyConfig(config);
38079 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38081 getPanelId : function(p){
38085 applyConfig : function(config){
38086 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38087 this.config = config;
38092 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38093 * the width, for horizontal (north, south) the height.
38094 * @param {Number} newSize The new width or height
38096 resizeTo : function(newSize){
38097 var el = this.el ? this.el :
38098 (this.activePanel ? this.activePanel.getEl() : null);
38100 switch(this.position){
38103 el.setWidth(newSize);
38104 this.fireEvent("resized", this, newSize);
38108 el.setHeight(newSize);
38109 this.fireEvent("resized", this, newSize);
38115 getBox : function(){
38116 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38119 getMargins : function(){
38120 return this.margins;
38123 updateBox : function(box){
38125 var el = this.activePanel.getEl();
38126 el.dom.style.left = box.x + "px";
38127 el.dom.style.top = box.y + "px";
38128 this.activePanel.setSize(box.width, box.height);
38132 * Returns the container element for this region.
38133 * @return {Roo.Element}
38135 getEl : function(){
38136 return this.activePanel;
38140 * Returns true if this region is currently visible.
38141 * @return {Boolean}
38143 isVisible : function(){
38144 return this.activePanel ? true : false;
38147 setActivePanel : function(panel){
38148 panel = this.getPanel(panel);
38149 if(this.activePanel && this.activePanel != panel){
38150 this.activePanel.setActiveState(false);
38151 this.activePanel.getEl().setLeftTop(-10000,-10000);
38153 this.activePanel = panel;
38154 panel.setActiveState(true);
38156 panel.setSize(this.box.width, this.box.height);
38158 this.fireEvent("panelactivated", this, panel);
38159 this.fireEvent("invalidated");
38163 * Show the specified panel.
38164 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38165 * @return {Roo.ContentPanel} The shown panel or null
38167 showPanel : function(panel){
38168 panel = this.getPanel(panel);
38170 this.setActivePanel(panel);
38176 * Get the active panel for this region.
38177 * @return {Roo.ContentPanel} The active panel or null
38179 getActivePanel : function(){
38180 return this.activePanel;
38184 * Add the passed ContentPanel(s)
38185 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38186 * @return {Roo.ContentPanel} The panel added (if only one was added)
38188 add : function(panel){
38189 if(arguments.length > 1){
38190 for(var i = 0, len = arguments.length; i < len; i++) {
38191 this.add(arguments[i]);
38195 if(this.hasPanel(panel)){
38196 this.showPanel(panel);
38199 var el = panel.getEl();
38200 if(el.dom.parentNode != this.mgr.el.dom){
38201 this.mgr.el.dom.appendChild(el.dom);
38203 if(panel.setRegion){
38204 panel.setRegion(this);
38206 this.panels.add(panel);
38207 el.setStyle("position", "absolute");
38208 if(!panel.background){
38209 this.setActivePanel(panel);
38210 if(this.config.initialSize && this.panels.getCount()==1){
38211 this.resizeTo(this.config.initialSize);
38214 this.fireEvent("paneladded", this, panel);
38219 * Returns true if the panel is in this region.
38220 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38221 * @return {Boolean}
38223 hasPanel : function(panel){
38224 if(typeof panel == "object"){ // must be panel obj
38225 panel = panel.getId();
38227 return this.getPanel(panel) ? true : false;
38231 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38232 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38233 * @param {Boolean} preservePanel Overrides the config preservePanel option
38234 * @return {Roo.ContentPanel} The panel that was removed
38236 remove : function(panel, preservePanel){
38237 panel = this.getPanel(panel);
38242 this.fireEvent("beforeremove", this, panel, e);
38243 if(e.cancel === true){
38246 var panelId = panel.getId();
38247 this.panels.removeKey(panelId);
38252 * Returns the panel specified or null if it's not in this region.
38253 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38254 * @return {Roo.ContentPanel}
38256 getPanel : function(id){
38257 if(typeof id == "object"){ // must be panel obj
38260 return this.panels.get(id);
38264 * Returns this regions position (north/south/east/west/center).
38267 getPosition: function(){
38268 return this.position;
38272 * Ext JS Library 1.1.1
38273 * Copyright(c) 2006-2007, Ext JS, LLC.
38275 * Originally Released Under LGPL - original licence link has changed is not relivant.
38278 * <script type="text/javascript">
38282 * @class Roo.bootstrap.layout.Region
38283 * @extends Roo.bootstrap.layout.Basic
38284 * This class represents a region in a layout manager.
38286 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38287 * @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})
38288 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38289 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38290 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38291 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38292 * @cfg {String} title The title for the region (overrides panel titles)
38293 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38294 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38295 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38296 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38297 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38298 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38299 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38300 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38301 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38302 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38304 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38305 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38306 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38307 * @cfg {Number} width For East/West panels
38308 * @cfg {Number} height For North/South panels
38309 * @cfg {Boolean} split To show the splitter
38310 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38312 * @cfg {string} cls Extra CSS classes to add to region
38314 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38315 * @cfg {string} region the region that it inhabits..
38318 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38319 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38321 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38322 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38323 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38325 Roo.bootstrap.layout.Region = function(config)
38327 this.applyConfig(config);
38329 var mgr = config.mgr;
38330 var pos = config.region;
38331 config.skipConfig = true;
38332 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38335 this.onRender(mgr.el);
38338 this.visible = true;
38339 this.collapsed = false;
38340 this.unrendered_panels = [];
38343 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38345 position: '', // set by wrapper (eg. north/south etc..)
38346 unrendered_panels : null, // unrendered panels.
38348 tabPosition : false,
38350 mgr: false, // points to 'Border'
38353 createBody : function(){
38354 /** This region's body element
38355 * @type Roo.Element */
38356 this.bodyEl = this.el.createChild({
38358 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38362 onRender: function(ctr, pos)
38364 var dh = Roo.DomHelper;
38365 /** This region's container element
38366 * @type Roo.Element */
38367 this.el = dh.append(ctr.dom, {
38369 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38371 /** This region's title element
38372 * @type Roo.Element */
38374 this.titleEl = dh.append(this.el.dom, {
38376 unselectable: "on",
38377 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38379 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38380 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38384 this.titleEl.enableDisplayMode();
38385 /** This region's title text element
38386 * @type HTMLElement */
38387 this.titleTextEl = this.titleEl.dom.firstChild;
38388 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38390 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38391 this.closeBtn.enableDisplayMode();
38392 this.closeBtn.on("click", this.closeClicked, this);
38393 this.closeBtn.hide();
38395 this.createBody(this.config);
38396 if(this.config.hideWhenEmpty){
38398 this.on("paneladded", this.validateVisibility, this);
38399 this.on("panelremoved", this.validateVisibility, this);
38401 if(this.autoScroll){
38402 this.bodyEl.setStyle("overflow", "auto");
38404 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38406 //if(c.titlebar !== false){
38407 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38408 this.titleEl.hide();
38410 this.titleEl.show();
38411 if(this.config.title){
38412 this.titleTextEl.innerHTML = this.config.title;
38416 if(this.config.collapsed){
38417 this.collapse(true);
38419 if(this.config.hidden){
38423 if (this.unrendered_panels && this.unrendered_panels.length) {
38424 for (var i =0;i< this.unrendered_panels.length; i++) {
38425 this.add(this.unrendered_panels[i]);
38427 this.unrendered_panels = null;
38433 applyConfig : function(c)
38436 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38437 var dh = Roo.DomHelper;
38438 if(c.titlebar !== false){
38439 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38440 this.collapseBtn.on("click", this.collapse, this);
38441 this.collapseBtn.enableDisplayMode();
38443 if(c.showPin === true || this.showPin){
38444 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38445 this.stickBtn.enableDisplayMode();
38446 this.stickBtn.on("click", this.expand, this);
38447 this.stickBtn.hide();
38452 /** This region's collapsed element
38453 * @type Roo.Element */
38456 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38457 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38460 if(c.floatable !== false){
38461 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38462 this.collapsedEl.on("click", this.collapseClick, this);
38465 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38466 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38467 id: "message", unselectable: "on", style:{"float":"left"}});
38468 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38470 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38471 this.expandBtn.on("click", this.expand, this);
38475 if(this.collapseBtn){
38476 this.collapseBtn.setVisible(c.collapsible == true);
38479 this.cmargins = c.cmargins || this.cmargins ||
38480 (this.position == "west" || this.position == "east" ?
38481 {top: 0, left: 2, right:2, bottom: 0} :
38482 {top: 2, left: 0, right:0, bottom: 2});
38484 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38487 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38489 this.autoScroll = c.autoScroll || false;
38494 this.duration = c.duration || .30;
38495 this.slideDuration = c.slideDuration || .45;
38500 * Returns true if this region is currently visible.
38501 * @return {Boolean}
38503 isVisible : function(){
38504 return this.visible;
38508 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38509 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38511 //setCollapsedTitle : function(title){
38512 // title = title || " ";
38513 // if(this.collapsedTitleTextEl){
38514 // this.collapsedTitleTextEl.innerHTML = title;
38518 getBox : function(){
38520 // if(!this.collapsed){
38521 b = this.el.getBox(false, true);
38523 // b = this.collapsedEl.getBox(false, true);
38528 getMargins : function(){
38529 return this.margins;
38530 //return this.collapsed ? this.cmargins : this.margins;
38533 highlight : function(){
38534 this.el.addClass("x-layout-panel-dragover");
38537 unhighlight : function(){
38538 this.el.removeClass("x-layout-panel-dragover");
38541 updateBox : function(box)
38543 if (!this.bodyEl) {
38544 return; // not rendered yet..
38548 if(!this.collapsed){
38549 this.el.dom.style.left = box.x + "px";
38550 this.el.dom.style.top = box.y + "px";
38551 this.updateBody(box.width, box.height);
38553 this.collapsedEl.dom.style.left = box.x + "px";
38554 this.collapsedEl.dom.style.top = box.y + "px";
38555 this.collapsedEl.setSize(box.width, box.height);
38558 this.tabs.autoSizeTabs();
38562 updateBody : function(w, h)
38565 this.el.setWidth(w);
38566 w -= this.el.getBorderWidth("rl");
38567 if(this.config.adjustments){
38568 w += this.config.adjustments[0];
38571 if(h !== null && h > 0){
38572 this.el.setHeight(h);
38573 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38574 h -= this.el.getBorderWidth("tb");
38575 if(this.config.adjustments){
38576 h += this.config.adjustments[1];
38578 this.bodyEl.setHeight(h);
38580 h = this.tabs.syncHeight(h);
38583 if(this.panelSize){
38584 w = w !== null ? w : this.panelSize.width;
38585 h = h !== null ? h : this.panelSize.height;
38587 if(this.activePanel){
38588 var el = this.activePanel.getEl();
38589 w = w !== null ? w : el.getWidth();
38590 h = h !== null ? h : el.getHeight();
38591 this.panelSize = {width: w, height: h};
38592 this.activePanel.setSize(w, h);
38594 if(Roo.isIE && this.tabs){
38595 this.tabs.el.repaint();
38600 * Returns the container element for this region.
38601 * @return {Roo.Element}
38603 getEl : function(){
38608 * Hides this region.
38611 //if(!this.collapsed){
38612 this.el.dom.style.left = "-2000px";
38615 // this.collapsedEl.dom.style.left = "-2000px";
38616 // this.collapsedEl.hide();
38618 this.visible = false;
38619 this.fireEvent("visibilitychange", this, false);
38623 * Shows this region if it was previously hidden.
38626 //if(!this.collapsed){
38629 // this.collapsedEl.show();
38631 this.visible = true;
38632 this.fireEvent("visibilitychange", this, true);
38635 closeClicked : function(){
38636 if(this.activePanel){
38637 this.remove(this.activePanel);
38641 collapseClick : function(e){
38643 e.stopPropagation();
38646 e.stopPropagation();
38652 * Collapses this region.
38653 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38656 collapse : function(skipAnim, skipCheck = false){
38657 if(this.collapsed) {
38661 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38663 this.collapsed = true;
38665 this.split.el.hide();
38667 if(this.config.animate && skipAnim !== true){
38668 this.fireEvent("invalidated", this);
38669 this.animateCollapse();
38671 this.el.setLocation(-20000,-20000);
38673 this.collapsedEl.show();
38674 this.fireEvent("collapsed", this);
38675 this.fireEvent("invalidated", this);
38681 animateCollapse : function(){
38686 * Expands this region if it was previously collapsed.
38687 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38688 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38691 expand : function(e, skipAnim){
38693 e.stopPropagation();
38695 if(!this.collapsed || this.el.hasActiveFx()) {
38699 this.afterSlideIn();
38702 this.collapsed = false;
38703 if(this.config.animate && skipAnim !== true){
38704 this.animateExpand();
38708 this.split.el.show();
38710 this.collapsedEl.setLocation(-2000,-2000);
38711 this.collapsedEl.hide();
38712 this.fireEvent("invalidated", this);
38713 this.fireEvent("expanded", this);
38717 animateExpand : function(){
38721 initTabs : function()
38723 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38725 var ts = new Roo.bootstrap.panel.Tabs({
38726 el: this.bodyEl.dom,
38728 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38729 disableTooltips: this.config.disableTabTips,
38730 toolbar : this.config.toolbar
38733 if(this.config.hideTabs){
38734 ts.stripWrap.setDisplayed(false);
38737 ts.resizeTabs = this.config.resizeTabs === true;
38738 ts.minTabWidth = this.config.minTabWidth || 40;
38739 ts.maxTabWidth = this.config.maxTabWidth || 250;
38740 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38741 ts.monitorResize = false;
38742 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38743 ts.bodyEl.addClass('roo-layout-tabs-body');
38744 this.panels.each(this.initPanelAsTab, this);
38747 initPanelAsTab : function(panel){
38748 var ti = this.tabs.addTab(
38752 this.config.closeOnTab && panel.isClosable(),
38755 if(panel.tabTip !== undefined){
38756 ti.setTooltip(panel.tabTip);
38758 ti.on("activate", function(){
38759 this.setActivePanel(panel);
38762 if(this.config.closeOnTab){
38763 ti.on("beforeclose", function(t, e){
38765 this.remove(panel);
38769 panel.tabItem = ti;
38774 updatePanelTitle : function(panel, title)
38776 if(this.activePanel == panel){
38777 this.updateTitle(title);
38780 var ti = this.tabs.getTab(panel.getEl().id);
38782 if(panel.tabTip !== undefined){
38783 ti.setTooltip(panel.tabTip);
38788 updateTitle : function(title){
38789 if(this.titleTextEl && !this.config.title){
38790 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38794 setActivePanel : function(panel)
38796 panel = this.getPanel(panel);
38797 if(this.activePanel && this.activePanel != panel){
38798 if(this.activePanel.setActiveState(false) === false){
38802 this.activePanel = panel;
38803 panel.setActiveState(true);
38804 if(this.panelSize){
38805 panel.setSize(this.panelSize.width, this.panelSize.height);
38808 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38810 this.updateTitle(panel.getTitle());
38812 this.fireEvent("invalidated", this);
38814 this.fireEvent("panelactivated", this, panel);
38818 * Shows the specified panel.
38819 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38820 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38822 showPanel : function(panel)
38824 panel = this.getPanel(panel);
38827 var tab = this.tabs.getTab(panel.getEl().id);
38828 if(tab.isHidden()){
38829 this.tabs.unhideTab(tab.id);
38833 this.setActivePanel(panel);
38840 * Get the active panel for this region.
38841 * @return {Roo.ContentPanel} The active panel or null
38843 getActivePanel : function(){
38844 return this.activePanel;
38847 validateVisibility : function(){
38848 if(this.panels.getCount() < 1){
38849 this.updateTitle(" ");
38850 this.closeBtn.hide();
38853 if(!this.isVisible()){
38860 * Adds the passed ContentPanel(s) to this region.
38861 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38862 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38864 add : function(panel)
38866 if(arguments.length > 1){
38867 for(var i = 0, len = arguments.length; i < len; i++) {
38868 this.add(arguments[i]);
38873 // if we have not been rendered yet, then we can not really do much of this..
38874 if (!this.bodyEl) {
38875 this.unrendered_panels.push(panel);
38882 if(this.hasPanel(panel)){
38883 this.showPanel(panel);
38886 panel.setRegion(this);
38887 this.panels.add(panel);
38888 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38889 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38890 // and hide them... ???
38891 this.bodyEl.dom.appendChild(panel.getEl().dom);
38892 if(panel.background !== true){
38893 this.setActivePanel(panel);
38895 this.fireEvent("paneladded", this, panel);
38902 this.initPanelAsTab(panel);
38906 if(panel.background !== true){
38907 this.tabs.activate(panel.getEl().id);
38909 this.fireEvent("paneladded", this, panel);
38914 * Hides the tab for the specified panel.
38915 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38917 hidePanel : function(panel){
38918 if(this.tabs && (panel = this.getPanel(panel))){
38919 this.tabs.hideTab(panel.getEl().id);
38924 * Unhides the tab for a previously hidden panel.
38925 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38927 unhidePanel : function(panel){
38928 if(this.tabs && (panel = this.getPanel(panel))){
38929 this.tabs.unhideTab(panel.getEl().id);
38933 clearPanels : function(){
38934 while(this.panels.getCount() > 0){
38935 this.remove(this.panels.first());
38940 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38941 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38942 * @param {Boolean} preservePanel Overrides the config preservePanel option
38943 * @return {Roo.ContentPanel} The panel that was removed
38945 remove : function(panel, preservePanel)
38947 panel = this.getPanel(panel);
38952 this.fireEvent("beforeremove", this, panel, e);
38953 if(e.cancel === true){
38956 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38957 var panelId = panel.getId();
38958 this.panels.removeKey(panelId);
38960 document.body.appendChild(panel.getEl().dom);
38963 this.tabs.removeTab(panel.getEl().id);
38964 }else if (!preservePanel){
38965 this.bodyEl.dom.removeChild(panel.getEl().dom);
38967 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38968 var p = this.panels.first();
38969 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38970 tempEl.appendChild(p.getEl().dom);
38971 this.bodyEl.update("");
38972 this.bodyEl.dom.appendChild(p.getEl().dom);
38974 this.updateTitle(p.getTitle());
38976 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38977 this.setActivePanel(p);
38979 panel.setRegion(null);
38980 if(this.activePanel == panel){
38981 this.activePanel = null;
38983 if(this.config.autoDestroy !== false && preservePanel !== true){
38984 try{panel.destroy();}catch(e){}
38986 this.fireEvent("panelremoved", this, panel);
38991 * Returns the TabPanel component used by this region
38992 * @return {Roo.TabPanel}
38994 getTabs : function(){
38998 createTool : function(parentEl, className){
38999 var btn = Roo.DomHelper.append(parentEl, {
39001 cls: "x-layout-tools-button",
39004 cls: "roo-layout-tools-button-inner " + className,
39008 btn.addClassOnOver("roo-layout-tools-button-over");
39013 * Ext JS Library 1.1.1
39014 * Copyright(c) 2006-2007, Ext JS, LLC.
39016 * Originally Released Under LGPL - original licence link has changed is not relivant.
39019 * <script type="text/javascript">
39025 * @class Roo.SplitLayoutRegion
39026 * @extends Roo.LayoutRegion
39027 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39029 Roo.bootstrap.layout.Split = function(config){
39030 this.cursor = config.cursor;
39031 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39034 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39036 splitTip : "Drag to resize.",
39037 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39038 useSplitTips : false,
39040 applyConfig : function(config){
39041 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39044 onRender : function(ctr,pos) {
39046 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39047 if(!this.config.split){
39052 var splitEl = Roo.DomHelper.append(ctr.dom, {
39054 id: this.el.id + "-split",
39055 cls: "roo-layout-split roo-layout-split-"+this.position,
39058 /** The SplitBar for this region
39059 * @type Roo.SplitBar */
39060 // does not exist yet...
39061 Roo.log([this.position, this.orientation]);
39063 this.split = new Roo.bootstrap.SplitBar({
39064 dragElement : splitEl,
39065 resizingElement: this.el,
39066 orientation : this.orientation
39069 this.split.on("moved", this.onSplitMove, this);
39070 this.split.useShim = this.config.useShim === true;
39071 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39072 if(this.useSplitTips){
39073 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39075 //if(config.collapsible){
39076 // this.split.el.on("dblclick", this.collapse, this);
39079 if(typeof this.config.minSize != "undefined"){
39080 this.split.minSize = this.config.minSize;
39082 if(typeof this.config.maxSize != "undefined"){
39083 this.split.maxSize = this.config.maxSize;
39085 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39086 this.hideSplitter();
39091 getHMaxSize : function(){
39092 var cmax = this.config.maxSize || 10000;
39093 var center = this.mgr.getRegion("center");
39094 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39097 getVMaxSize : function(){
39098 var cmax = this.config.maxSize || 10000;
39099 var center = this.mgr.getRegion("center");
39100 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39103 onSplitMove : function(split, newSize){
39104 this.fireEvent("resized", this, newSize);
39108 * Returns the {@link Roo.SplitBar} for this region.
39109 * @return {Roo.SplitBar}
39111 getSplitBar : function(){
39116 this.hideSplitter();
39117 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39120 hideSplitter : function(){
39122 this.split.el.setLocation(-2000,-2000);
39123 this.split.el.hide();
39129 this.split.el.show();
39131 Roo.bootstrap.layout.Split.superclass.show.call(this);
39134 beforeSlide: function(){
39135 if(Roo.isGecko){// firefox overflow auto bug workaround
39136 this.bodyEl.clip();
39138 this.tabs.bodyEl.clip();
39140 if(this.activePanel){
39141 this.activePanel.getEl().clip();
39143 if(this.activePanel.beforeSlide){
39144 this.activePanel.beforeSlide();
39150 afterSlide : function(){
39151 if(Roo.isGecko){// firefox overflow auto bug workaround
39152 this.bodyEl.unclip();
39154 this.tabs.bodyEl.unclip();
39156 if(this.activePanel){
39157 this.activePanel.getEl().unclip();
39158 if(this.activePanel.afterSlide){
39159 this.activePanel.afterSlide();
39165 initAutoHide : function(){
39166 if(this.autoHide !== false){
39167 if(!this.autoHideHd){
39168 var st = new Roo.util.DelayedTask(this.slideIn, this);
39169 this.autoHideHd = {
39170 "mouseout": function(e){
39171 if(!e.within(this.el, true)){
39175 "mouseover" : function(e){
39181 this.el.on(this.autoHideHd);
39185 clearAutoHide : function(){
39186 if(this.autoHide !== false){
39187 this.el.un("mouseout", this.autoHideHd.mouseout);
39188 this.el.un("mouseover", this.autoHideHd.mouseover);
39192 clearMonitor : function(){
39193 Roo.get(document).un("click", this.slideInIf, this);
39196 // these names are backwards but not changed for compat
39197 slideOut : function(){
39198 if(this.isSlid || this.el.hasActiveFx()){
39201 this.isSlid = true;
39202 if(this.collapseBtn){
39203 this.collapseBtn.hide();
39205 this.closeBtnState = this.closeBtn.getStyle('display');
39206 this.closeBtn.hide();
39208 this.stickBtn.show();
39211 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39212 this.beforeSlide();
39213 this.el.setStyle("z-index", 10001);
39214 this.el.slideIn(this.getSlideAnchor(), {
39215 callback: function(){
39217 this.initAutoHide();
39218 Roo.get(document).on("click", this.slideInIf, this);
39219 this.fireEvent("slideshow", this);
39226 afterSlideIn : function(){
39227 this.clearAutoHide();
39228 this.isSlid = false;
39229 this.clearMonitor();
39230 this.el.setStyle("z-index", "");
39231 if(this.collapseBtn){
39232 this.collapseBtn.show();
39234 this.closeBtn.setStyle('display', this.closeBtnState);
39236 this.stickBtn.hide();
39238 this.fireEvent("slidehide", this);
39241 slideIn : function(cb){
39242 if(!this.isSlid || this.el.hasActiveFx()){
39246 this.isSlid = false;
39247 this.beforeSlide();
39248 this.el.slideOut(this.getSlideAnchor(), {
39249 callback: function(){
39250 this.el.setLeftTop(-10000, -10000);
39252 this.afterSlideIn();
39260 slideInIf : function(e){
39261 if(!e.within(this.el)){
39266 animateCollapse : function(){
39267 this.beforeSlide();
39268 this.el.setStyle("z-index", 20000);
39269 var anchor = this.getSlideAnchor();
39270 this.el.slideOut(anchor, {
39271 callback : function(){
39272 this.el.setStyle("z-index", "");
39273 this.collapsedEl.slideIn(anchor, {duration:.3});
39275 this.el.setLocation(-10000,-10000);
39277 this.fireEvent("collapsed", this);
39284 animateExpand : function(){
39285 this.beforeSlide();
39286 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39287 this.el.setStyle("z-index", 20000);
39288 this.collapsedEl.hide({
39291 this.el.slideIn(this.getSlideAnchor(), {
39292 callback : function(){
39293 this.el.setStyle("z-index", "");
39296 this.split.el.show();
39298 this.fireEvent("invalidated", this);
39299 this.fireEvent("expanded", this);
39327 getAnchor : function(){
39328 return this.anchors[this.position];
39331 getCollapseAnchor : function(){
39332 return this.canchors[this.position];
39335 getSlideAnchor : function(){
39336 return this.sanchors[this.position];
39339 getAlignAdj : function(){
39340 var cm = this.cmargins;
39341 switch(this.position){
39357 getExpandAdj : function(){
39358 var c = this.collapsedEl, cm = this.cmargins;
39359 switch(this.position){
39361 return [-(cm.right+c.getWidth()+cm.left), 0];
39364 return [cm.right+c.getWidth()+cm.left, 0];
39367 return [0, -(cm.top+cm.bottom+c.getHeight())];
39370 return [0, cm.top+cm.bottom+c.getHeight()];
39376 * Ext JS Library 1.1.1
39377 * Copyright(c) 2006-2007, Ext JS, LLC.
39379 * Originally Released Under LGPL - original licence link has changed is not relivant.
39382 * <script type="text/javascript">
39385 * These classes are private internal classes
39387 Roo.bootstrap.layout.Center = function(config){
39388 config.region = "center";
39389 Roo.bootstrap.layout.Region.call(this, config);
39390 this.visible = true;
39391 this.minWidth = config.minWidth || 20;
39392 this.minHeight = config.minHeight || 20;
39395 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39397 // center panel can't be hidden
39401 // center panel can't be hidden
39404 getMinWidth: function(){
39405 return this.minWidth;
39408 getMinHeight: function(){
39409 return this.minHeight;
39423 Roo.bootstrap.layout.North = function(config)
39425 config.region = 'north';
39426 config.cursor = 'n-resize';
39428 Roo.bootstrap.layout.Split.call(this, config);
39432 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39433 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39434 this.split.el.addClass("roo-layout-split-v");
39436 //var size = config.initialSize || config.height;
39437 //if(this.el && typeof size != "undefined"){
39438 // this.el.setHeight(size);
39441 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39443 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39446 onRender : function(ctr, pos)
39448 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39449 var size = this.config.initialSize || this.config.height;
39450 if(this.el && typeof size != "undefined"){
39451 this.el.setHeight(size);
39456 getBox : function(){
39457 if(this.collapsed){
39458 return this.collapsedEl.getBox();
39460 var box = this.el.getBox();
39462 box.height += this.split.el.getHeight();
39467 updateBox : function(box){
39468 if(this.split && !this.collapsed){
39469 box.height -= this.split.el.getHeight();
39470 this.split.el.setLeft(box.x);
39471 this.split.el.setTop(box.y+box.height);
39472 this.split.el.setWidth(box.width);
39474 if(this.collapsed){
39475 this.updateBody(box.width, null);
39477 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39485 Roo.bootstrap.layout.South = function(config){
39486 config.region = 'south';
39487 config.cursor = 's-resize';
39488 Roo.bootstrap.layout.Split.call(this, config);
39490 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39491 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39492 this.split.el.addClass("roo-layout-split-v");
39497 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39498 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39500 onRender : function(ctr, pos)
39502 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39503 var size = this.config.initialSize || this.config.height;
39504 if(this.el && typeof size != "undefined"){
39505 this.el.setHeight(size);
39510 getBox : function(){
39511 if(this.collapsed){
39512 return this.collapsedEl.getBox();
39514 var box = this.el.getBox();
39516 var sh = this.split.el.getHeight();
39523 updateBox : function(box){
39524 if(this.split && !this.collapsed){
39525 var sh = this.split.el.getHeight();
39528 this.split.el.setLeft(box.x);
39529 this.split.el.setTop(box.y-sh);
39530 this.split.el.setWidth(box.width);
39532 if(this.collapsed){
39533 this.updateBody(box.width, null);
39535 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39539 Roo.bootstrap.layout.East = function(config){
39540 config.region = "east";
39541 config.cursor = "e-resize";
39542 Roo.bootstrap.layout.Split.call(this, config);
39544 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39545 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39546 this.split.el.addClass("roo-layout-split-h");
39550 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39551 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39553 onRender : function(ctr, pos)
39555 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39556 var size = this.config.initialSize || this.config.width;
39557 if(this.el && typeof size != "undefined"){
39558 this.el.setWidth(size);
39563 getBox : function(){
39564 if(this.collapsed){
39565 return this.collapsedEl.getBox();
39567 var box = this.el.getBox();
39569 var sw = this.split.el.getWidth();
39576 updateBox : function(box){
39577 if(this.split && !this.collapsed){
39578 var sw = this.split.el.getWidth();
39580 this.split.el.setLeft(box.x);
39581 this.split.el.setTop(box.y);
39582 this.split.el.setHeight(box.height);
39585 if(this.collapsed){
39586 this.updateBody(null, box.height);
39588 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39592 Roo.bootstrap.layout.West = function(config){
39593 config.region = "west";
39594 config.cursor = "w-resize";
39596 Roo.bootstrap.layout.Split.call(this, config);
39598 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39599 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39600 this.split.el.addClass("roo-layout-split-h");
39604 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39605 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39607 onRender: function(ctr, pos)
39609 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39610 var size = this.config.initialSize || this.config.width;
39611 if(typeof size != "undefined"){
39612 this.el.setWidth(size);
39616 getBox : function(){
39617 if(this.collapsed){
39618 return this.collapsedEl.getBox();
39620 var box = this.el.getBox();
39621 if (box.width == 0) {
39622 box.width = this.config.width; // kludge?
39625 box.width += this.split.el.getWidth();
39630 updateBox : function(box){
39631 if(this.split && !this.collapsed){
39632 var sw = this.split.el.getWidth();
39634 this.split.el.setLeft(box.x+box.width);
39635 this.split.el.setTop(box.y);
39636 this.split.el.setHeight(box.height);
39638 if(this.collapsed){
39639 this.updateBody(null, box.height);
39641 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39643 });Roo.namespace("Roo.bootstrap.panel");/*
39645 * Ext JS Library 1.1.1
39646 * Copyright(c) 2006-2007, Ext JS, LLC.
39648 * Originally Released Under LGPL - original licence link has changed is not relivant.
39651 * <script type="text/javascript">
39654 * @class Roo.ContentPanel
39655 * @extends Roo.util.Observable
39656 * A basic ContentPanel element.
39657 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39658 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39659 * @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
39660 * @cfg {Boolean} closable True if the panel can be closed/removed
39661 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39662 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39663 * @cfg {Toolbar} toolbar A toolbar for this panel
39664 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39665 * @cfg {String} title The title for this panel
39666 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39667 * @cfg {String} url Calls {@link #setUrl} with this value
39668 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39669 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39670 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39671 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39672 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39673 * @cfg {Boolean} badges render the badges
39674 * @cfg {String} cls extra classes to use
39675 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39678 * Create a new ContentPanel.
39679 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39680 * @param {String/Object} config A string to set only the title or a config object
39681 * @param {String} content (optional) Set the HTML content for this panel
39682 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39684 Roo.bootstrap.panel.Content = function( config){
39686 this.tpl = config.tpl || false;
39688 var el = config.el;
39689 var content = config.content;
39691 if(config.autoCreate){ // xtype is available if this is called from factory
39694 this.el = Roo.get(el);
39695 if(!this.el && config && config.autoCreate){
39696 if(typeof config.autoCreate == "object"){
39697 if(!config.autoCreate.id){
39698 config.autoCreate.id = config.id||el;
39700 this.el = Roo.DomHelper.append(document.body,
39701 config.autoCreate, true);
39705 cls: (config.cls || '') +
39706 (config.background ? ' bg-' + config.background : '') +
39707 " roo-layout-inactive-content",
39710 if (config.iframe) {
39714 style : 'border: 0px',
39715 src : 'about:blank'
39721 elcfg.html = config.html;
39725 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39726 if (config.iframe) {
39727 this.iframeEl = this.el.select('iframe',true).first();
39732 this.closable = false;
39733 this.loaded = false;
39734 this.active = false;
39737 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39739 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39741 this.wrapEl = this.el; //this.el.wrap();
39743 if (config.toolbar.items) {
39744 ti = config.toolbar.items ;
39745 delete config.toolbar.items ;
39749 this.toolbar.render(this.wrapEl, 'before');
39750 for(var i =0;i < ti.length;i++) {
39751 // Roo.log(['add child', items[i]]);
39752 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39754 this.toolbar.items = nitems;
39755 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39756 delete config.toolbar;
39760 // xtype created footer. - not sure if will work as we normally have to render first..
39761 if (this.footer && !this.footer.el && this.footer.xtype) {
39762 if (!this.wrapEl) {
39763 this.wrapEl = this.el.wrap();
39766 this.footer.container = this.wrapEl.createChild();
39768 this.footer = Roo.factory(this.footer, Roo);
39773 if(typeof config == "string"){
39774 this.title = config;
39776 Roo.apply(this, config);
39780 this.resizeEl = Roo.get(this.resizeEl, true);
39782 this.resizeEl = this.el;
39784 // handle view.xtype
39792 * Fires when this panel is activated.
39793 * @param {Roo.ContentPanel} this
39797 * @event deactivate
39798 * Fires when this panel is activated.
39799 * @param {Roo.ContentPanel} this
39801 "deactivate" : true,
39805 * Fires when this panel is resized if fitToFrame is true.
39806 * @param {Roo.ContentPanel} this
39807 * @param {Number} width The width after any component adjustments
39808 * @param {Number} height The height after any component adjustments
39814 * Fires when this tab is created
39815 * @param {Roo.ContentPanel} this
39826 if(this.autoScroll && !this.iframe){
39827 this.resizeEl.setStyle("overflow", "auto");
39829 // fix randome scrolling
39830 //this.el.on('scroll', function() {
39831 // Roo.log('fix random scolling');
39832 // this.scrollTo('top',0);
39835 content = content || this.content;
39837 this.setContent(content);
39839 if(config && config.url){
39840 this.setUrl(this.url, this.params, this.loadOnce);
39845 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39847 if (this.view && typeof(this.view.xtype) != 'undefined') {
39848 this.view.el = this.el.appendChild(document.createElement("div"));
39849 this.view = Roo.factory(this.view);
39850 this.view.render && this.view.render(false, '');
39854 this.fireEvent('render', this);
39857 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39867 setRegion : function(region){
39868 this.region = region;
39869 this.setActiveClass(region && !this.background);
39873 setActiveClass: function(state)
39876 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39877 this.el.setStyle('position','relative');
39879 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39880 this.el.setStyle('position', 'absolute');
39885 * Returns the toolbar for this Panel if one was configured.
39886 * @return {Roo.Toolbar}
39888 getToolbar : function(){
39889 return this.toolbar;
39892 setActiveState : function(active)
39894 this.active = active;
39895 this.setActiveClass(active);
39897 if(this.fireEvent("deactivate", this) === false){
39902 this.fireEvent("activate", this);
39906 * Updates this panel's element (not for iframe)
39907 * @param {String} content The new content
39908 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39910 setContent : function(content, loadScripts){
39915 this.el.update(content, loadScripts);
39918 ignoreResize : function(w, h){
39919 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39922 this.lastSize = {width: w, height: h};
39927 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39928 * @return {Roo.UpdateManager} The UpdateManager
39930 getUpdateManager : function(){
39934 return this.el.getUpdateManager();
39937 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39938 * Does not work with IFRAME contents
39939 * @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:
39942 url: "your-url.php",
39943 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39944 callback: yourFunction,
39945 scope: yourObject, //(optional scope)
39948 text: "Loading...",
39954 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39955 * 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.
39956 * @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}
39957 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39958 * @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.
39959 * @return {Roo.ContentPanel} this
39967 var um = this.el.getUpdateManager();
39968 um.update.apply(um, arguments);
39974 * 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.
39975 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39976 * @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)
39977 * @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)
39978 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39980 setUrl : function(url, params, loadOnce){
39982 this.iframeEl.dom.src = url;
39986 if(this.refreshDelegate){
39987 this.removeListener("activate", this.refreshDelegate);
39989 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39990 this.on("activate", this.refreshDelegate);
39991 return this.el.getUpdateManager();
39994 _handleRefresh : function(url, params, loadOnce){
39995 if(!loadOnce || !this.loaded){
39996 var updater = this.el.getUpdateManager();
39997 updater.update(url, params, this._setLoaded.createDelegate(this));
40001 _setLoaded : function(){
40002 this.loaded = true;
40006 * Returns this panel's id
40009 getId : function(){
40014 * Returns this panel's element - used by regiosn to add.
40015 * @return {Roo.Element}
40017 getEl : function(){
40018 return this.wrapEl || this.el;
40023 adjustForComponents : function(width, height)
40025 //Roo.log('adjustForComponents ');
40026 if(this.resizeEl != this.el){
40027 width -= this.el.getFrameWidth('lr');
40028 height -= this.el.getFrameWidth('tb');
40031 var te = this.toolbar.getEl();
40032 te.setWidth(width);
40033 height -= te.getHeight();
40036 var te = this.footer.getEl();
40037 te.setWidth(width);
40038 height -= te.getHeight();
40042 if(this.adjustments){
40043 width += this.adjustments[0];
40044 height += this.adjustments[1];
40046 return {"width": width, "height": height};
40049 setSize : function(width, height){
40050 if(this.fitToFrame && !this.ignoreResize(width, height)){
40051 if(this.fitContainer && this.resizeEl != this.el){
40052 this.el.setSize(width, height);
40054 var size = this.adjustForComponents(width, height);
40056 this.iframeEl.setSize(width,height);
40059 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40060 this.fireEvent('resize', this, size.width, size.height);
40067 * Returns this panel's title
40070 getTitle : function(){
40072 if (typeof(this.title) != 'object') {
40077 for (var k in this.title) {
40078 if (!this.title.hasOwnProperty(k)) {
40082 if (k.indexOf('-') >= 0) {
40083 var s = k.split('-');
40084 for (var i = 0; i<s.length; i++) {
40085 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40088 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40095 * Set this panel's title
40096 * @param {String} title
40098 setTitle : function(title){
40099 this.title = title;
40101 this.region.updatePanelTitle(this, title);
40106 * Returns true is this panel was configured to be closable
40107 * @return {Boolean}
40109 isClosable : function(){
40110 return this.closable;
40113 beforeSlide : function(){
40115 this.resizeEl.clip();
40118 afterSlide : function(){
40120 this.resizeEl.unclip();
40124 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40125 * Will fail silently if the {@link #setUrl} method has not been called.
40126 * This does not activate the panel, just updates its content.
40128 refresh : function(){
40129 if(this.refreshDelegate){
40130 this.loaded = false;
40131 this.refreshDelegate();
40136 * Destroys this panel
40138 destroy : function(){
40139 this.el.removeAllListeners();
40140 var tempEl = document.createElement("span");
40141 tempEl.appendChild(this.el.dom);
40142 tempEl.innerHTML = "";
40148 * form - if the content panel contains a form - this is a reference to it.
40149 * @type {Roo.form.Form}
40153 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40154 * This contains a reference to it.
40160 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40170 * @param {Object} cfg Xtype definition of item to add.
40174 getChildContainer: function () {
40175 return this.getEl();
40180 var ret = new Roo.factory(cfg);
40185 if (cfg.xtype.match(/^Form$/)) {
40188 //if (this.footer) {
40189 // el = this.footer.container.insertSibling(false, 'before');
40191 el = this.el.createChild();
40194 this.form = new Roo.form.Form(cfg);
40197 if ( this.form.allItems.length) {
40198 this.form.render(el.dom);
40202 // should only have one of theses..
40203 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40204 // views.. should not be just added - used named prop 'view''
40206 cfg.el = this.el.appendChild(document.createElement("div"));
40209 var ret = new Roo.factory(cfg);
40211 ret.render && ret.render(false, ''); // render blank..
40221 * @class Roo.bootstrap.panel.Grid
40222 * @extends Roo.bootstrap.panel.Content
40224 * Create a new GridPanel.
40225 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40226 * @param {Object} config A the config object
40232 Roo.bootstrap.panel.Grid = function(config)
40236 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40237 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40239 config.el = this.wrapper;
40240 //this.el = this.wrapper;
40242 if (config.container) {
40243 // ctor'ed from a Border/panel.grid
40246 this.wrapper.setStyle("overflow", "hidden");
40247 this.wrapper.addClass('roo-grid-container');
40252 if(config.toolbar){
40253 var tool_el = this.wrapper.createChild();
40254 this.toolbar = Roo.factory(config.toolbar);
40256 if (config.toolbar.items) {
40257 ti = config.toolbar.items ;
40258 delete config.toolbar.items ;
40262 this.toolbar.render(tool_el);
40263 for(var i =0;i < ti.length;i++) {
40264 // Roo.log(['add child', items[i]]);
40265 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40267 this.toolbar.items = nitems;
40269 delete config.toolbar;
40272 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40273 config.grid.scrollBody = true;;
40274 config.grid.monitorWindowResize = false; // turn off autosizing
40275 config.grid.autoHeight = false;
40276 config.grid.autoWidth = false;
40278 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40280 if (config.background) {
40281 // render grid on panel activation (if panel background)
40282 this.on('activate', function(gp) {
40283 if (!gp.grid.rendered) {
40284 gp.grid.render(this.wrapper);
40285 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40290 this.grid.render(this.wrapper);
40291 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40294 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40295 // ??? needed ??? config.el = this.wrapper;
40300 // xtype created footer. - not sure if will work as we normally have to render first..
40301 if (this.footer && !this.footer.el && this.footer.xtype) {
40303 var ctr = this.grid.getView().getFooterPanel(true);
40304 this.footer.dataSource = this.grid.dataSource;
40305 this.footer = Roo.factory(this.footer, Roo);
40306 this.footer.render(ctr);
40316 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40317 getId : function(){
40318 return this.grid.id;
40322 * Returns the grid for this panel
40323 * @return {Roo.bootstrap.Table}
40325 getGrid : function(){
40329 setSize : function(width, height){
40330 if(!this.ignoreResize(width, height)){
40331 var grid = this.grid;
40332 var size = this.adjustForComponents(width, height);
40333 // tfoot is not a footer?
40336 var gridel = grid.getGridEl();
40337 gridel.setSize(size.width, size.height);
40339 var tbd = grid.getGridEl().select('tbody', true).first();
40340 var thd = grid.getGridEl().select('thead',true).first();
40341 var tbf= grid.getGridEl().select('tfoot', true).first();
40344 size.height -= tbf.getHeight();
40347 size.height -= thd.getHeight();
40350 tbd.setSize(size.width, size.height );
40351 // this is for the account management tab -seems to work there.
40352 var thd = grid.getGridEl().select('thead',true).first();
40354 // tbd.setSize(size.width, size.height - thd.getHeight());
40363 beforeSlide : function(){
40364 this.grid.getView().scroller.clip();
40367 afterSlide : function(){
40368 this.grid.getView().scroller.unclip();
40371 destroy : function(){
40372 this.grid.destroy();
40374 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40379 * @class Roo.bootstrap.panel.Nest
40380 * @extends Roo.bootstrap.panel.Content
40382 * Create a new Panel, that can contain a layout.Border.
40385 * @param {Roo.BorderLayout} layout The layout for this panel
40386 * @param {String/Object} config A string to set only the title or a config object
40388 Roo.bootstrap.panel.Nest = function(config)
40390 // construct with only one argument..
40391 /* FIXME - implement nicer consturctors
40392 if (layout.layout) {
40394 layout = config.layout;
40395 delete config.layout;
40397 if (layout.xtype && !layout.getEl) {
40398 // then layout needs constructing..
40399 layout = Roo.factory(layout, Roo);
40403 config.el = config.layout.getEl();
40405 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40407 config.layout.monitorWindowResize = false; // turn off autosizing
40408 this.layout = config.layout;
40409 this.layout.getEl().addClass("roo-layout-nested-layout");
40410 this.layout.parent = this;
40417 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40419 setSize : function(width, height){
40420 if(!this.ignoreResize(width, height)){
40421 var size = this.adjustForComponents(width, height);
40422 var el = this.layout.getEl();
40423 if (size.height < 1) {
40424 el.setWidth(size.width);
40426 el.setSize(size.width, size.height);
40428 var touch = el.dom.offsetWidth;
40429 this.layout.layout();
40430 // ie requires a double layout on the first pass
40431 if(Roo.isIE && !this.initialized){
40432 this.initialized = true;
40433 this.layout.layout();
40438 // activate all subpanels if not currently active..
40440 setActiveState : function(active){
40441 this.active = active;
40442 this.setActiveClass(active);
40445 this.fireEvent("deactivate", this);
40449 this.fireEvent("activate", this);
40450 // not sure if this should happen before or after..
40451 if (!this.layout) {
40452 return; // should not happen..
40455 for (var r in this.layout.regions) {
40456 reg = this.layout.getRegion(r);
40457 if (reg.getActivePanel()) {
40458 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40459 reg.setActivePanel(reg.getActivePanel());
40462 if (!reg.panels.length) {
40465 reg.showPanel(reg.getPanel(0));
40474 * Returns the nested BorderLayout for this panel
40475 * @return {Roo.BorderLayout}
40477 getLayout : function(){
40478 return this.layout;
40482 * Adds a xtype elements to the layout of the nested panel
40486 xtype : 'ContentPanel',
40493 xtype : 'NestedLayoutPanel',
40499 items : [ ... list of content panels or nested layout panels.. ]
40503 * @param {Object} cfg Xtype definition of item to add.
40505 addxtype : function(cfg) {
40506 return this.layout.addxtype(cfg);
40511 * Ext JS Library 1.1.1
40512 * Copyright(c) 2006-2007, Ext JS, LLC.
40514 * Originally Released Under LGPL - original licence link has changed is not relivant.
40517 * <script type="text/javascript">
40520 * @class Roo.TabPanel
40521 * @extends Roo.util.Observable
40522 * A lightweight tab container.
40526 // basic tabs 1, built from existing content
40527 var tabs = new Roo.TabPanel("tabs1");
40528 tabs.addTab("script", "View Script");
40529 tabs.addTab("markup", "View Markup");
40530 tabs.activate("script");
40532 // more advanced tabs, built from javascript
40533 var jtabs = new Roo.TabPanel("jtabs");
40534 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40536 // set up the UpdateManager
40537 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40538 var updater = tab2.getUpdateManager();
40539 updater.setDefaultUrl("ajax1.htm");
40540 tab2.on('activate', updater.refresh, updater, true);
40542 // Use setUrl for Ajax loading
40543 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40544 tab3.setUrl("ajax2.htm", null, true);
40547 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40550 jtabs.activate("jtabs-1");
40553 * Create a new TabPanel.
40554 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40555 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40557 Roo.bootstrap.panel.Tabs = function(config){
40559 * The container element for this TabPanel.
40560 * @type Roo.Element
40562 this.el = Roo.get(config.el);
40565 if(typeof config == "boolean"){
40566 this.tabPosition = config ? "bottom" : "top";
40568 Roo.apply(this, config);
40572 if(this.tabPosition == "bottom"){
40573 // if tabs are at the bottom = create the body first.
40574 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40575 this.el.addClass("roo-tabs-bottom");
40577 // next create the tabs holders
40579 if (this.tabPosition == "west"){
40581 var reg = this.region; // fake it..
40583 if (!reg.mgr.parent) {
40586 reg = reg.mgr.parent.region;
40588 Roo.log("got nest?");
40590 if (reg.mgr.getRegion('west')) {
40591 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40592 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40593 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40594 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40595 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40603 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40604 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40605 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40606 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40611 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40614 // finally - if tabs are at the top, then create the body last..
40615 if(this.tabPosition != "bottom"){
40616 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40617 * @type Roo.Element
40619 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40620 this.el.addClass("roo-tabs-top");
40624 this.bodyEl.setStyle("position", "relative");
40626 this.active = null;
40627 this.activateDelegate = this.activate.createDelegate(this);
40632 * Fires when the active tab changes
40633 * @param {Roo.TabPanel} this
40634 * @param {Roo.TabPanelItem} activePanel The new active tab
40638 * @event beforetabchange
40639 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40640 * @param {Roo.TabPanel} this
40641 * @param {Object} e Set cancel to true on this object to cancel the tab change
40642 * @param {Roo.TabPanelItem} tab The tab being changed to
40644 "beforetabchange" : true
40647 Roo.EventManager.onWindowResize(this.onResize, this);
40648 this.cpad = this.el.getPadding("lr");
40649 this.hiddenCount = 0;
40652 // toolbar on the tabbar support...
40653 if (this.toolbar) {
40654 alert("no toolbar support yet");
40655 this.toolbar = false;
40657 var tcfg = this.toolbar;
40658 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40659 this.toolbar = new Roo.Toolbar(tcfg);
40660 if (Roo.isSafari) {
40661 var tbl = tcfg.container.child('table', true);
40662 tbl.setAttribute('width', '100%');
40670 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40673 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40675 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40677 tabPosition : "top",
40679 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40681 currentTabWidth : 0,
40683 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40687 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40691 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40693 preferredTabWidth : 175,
40695 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40697 resizeTabs : false,
40699 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40701 monitorResize : true,
40703 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40705 toolbar : false, // set by caller..
40707 region : false, /// set by caller
40709 disableTooltips : true, // not used yet...
40712 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40713 * @param {String} id The id of the div to use <b>or create</b>
40714 * @param {String} text The text for the tab
40715 * @param {String} content (optional) Content to put in the TabPanelItem body
40716 * @param {Boolean} closable (optional) True to create a close icon on the tab
40717 * @return {Roo.TabPanelItem} The created TabPanelItem
40719 addTab : function(id, text, content, closable, tpl)
40721 var item = new Roo.bootstrap.panel.TabItem({
40725 closable : closable,
40728 this.addTabItem(item);
40730 item.setContent(content);
40736 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40737 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40738 * @return {Roo.TabPanelItem}
40740 getTab : function(id){
40741 return this.items[id];
40745 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40746 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40748 hideTab : function(id){
40749 var t = this.items[id];
40752 this.hiddenCount++;
40753 this.autoSizeTabs();
40758 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40759 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40761 unhideTab : function(id){
40762 var t = this.items[id];
40764 t.setHidden(false);
40765 this.hiddenCount--;
40766 this.autoSizeTabs();
40771 * Adds an existing {@link Roo.TabPanelItem}.
40772 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40774 addTabItem : function(item)
40776 this.items[item.id] = item;
40777 this.items.push(item);
40778 this.autoSizeTabs();
40779 // if(this.resizeTabs){
40780 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40781 // this.autoSizeTabs();
40783 // item.autoSize();
40788 * Removes a {@link Roo.TabPanelItem}.
40789 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40791 removeTab : function(id){
40792 var items = this.items;
40793 var tab = items[id];
40794 if(!tab) { return; }
40795 var index = items.indexOf(tab);
40796 if(this.active == tab && items.length > 1){
40797 var newTab = this.getNextAvailable(index);
40802 this.stripEl.dom.removeChild(tab.pnode.dom);
40803 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40804 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40806 items.splice(index, 1);
40807 delete this.items[tab.id];
40808 tab.fireEvent("close", tab);
40809 tab.purgeListeners();
40810 this.autoSizeTabs();
40813 getNextAvailable : function(start){
40814 var items = this.items;
40816 // look for a next tab that will slide over to
40817 // replace the one being removed
40818 while(index < items.length){
40819 var item = items[++index];
40820 if(item && !item.isHidden()){
40824 // if one isn't found select the previous tab (on the left)
40827 var item = items[--index];
40828 if(item && !item.isHidden()){
40836 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40837 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40839 disableTab : function(id){
40840 var tab = this.items[id];
40841 if(tab && this.active != tab){
40847 * Enables a {@link Roo.TabPanelItem} that is disabled.
40848 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40850 enableTab : function(id){
40851 var tab = this.items[id];
40856 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40857 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40858 * @return {Roo.TabPanelItem} The TabPanelItem.
40860 activate : function(id)
40862 //Roo.log('activite:' + id);
40864 var tab = this.items[id];
40868 if(tab == this.active || tab.disabled){
40872 this.fireEvent("beforetabchange", this, e, tab);
40873 if(e.cancel !== true && !tab.disabled){
40875 this.active.hide();
40877 this.active = this.items[id];
40878 this.active.show();
40879 this.fireEvent("tabchange", this, this.active);
40885 * Gets the active {@link Roo.TabPanelItem}.
40886 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40888 getActiveTab : function(){
40889 return this.active;
40893 * Updates the tab body element to fit the height of the container element
40894 * for overflow scrolling
40895 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40897 syncHeight : function(targetHeight){
40898 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40899 var bm = this.bodyEl.getMargins();
40900 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40901 this.bodyEl.setHeight(newHeight);
40905 onResize : function(){
40906 if(this.monitorResize){
40907 this.autoSizeTabs();
40912 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40914 beginUpdate : function(){
40915 this.updating = true;
40919 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40921 endUpdate : function(){
40922 this.updating = false;
40923 this.autoSizeTabs();
40927 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40929 autoSizeTabs : function()
40931 var count = this.items.length;
40932 var vcount = count - this.hiddenCount;
40935 this.stripEl.hide();
40937 this.stripEl.show();
40940 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40945 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40946 var availWidth = Math.floor(w / vcount);
40947 var b = this.stripBody;
40948 if(b.getWidth() > w){
40949 var tabs = this.items;
40950 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40951 if(availWidth < this.minTabWidth){
40952 /*if(!this.sleft){ // incomplete scrolling code
40953 this.createScrollButtons();
40956 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40959 if(this.currentTabWidth < this.preferredTabWidth){
40960 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40966 * Returns the number of tabs in this TabPanel.
40969 getCount : function(){
40970 return this.items.length;
40974 * Resizes all the tabs to the passed width
40975 * @param {Number} The new width
40977 setTabWidth : function(width){
40978 this.currentTabWidth = width;
40979 for(var i = 0, len = this.items.length; i < len; i++) {
40980 if(!this.items[i].isHidden()) {
40981 this.items[i].setWidth(width);
40987 * Destroys this TabPanel
40988 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40990 destroy : function(removeEl){
40991 Roo.EventManager.removeResizeListener(this.onResize, this);
40992 for(var i = 0, len = this.items.length; i < len; i++){
40993 this.items[i].purgeListeners();
40995 if(removeEl === true){
40996 this.el.update("");
41001 createStrip : function(container)
41003 var strip = document.createElement("nav");
41004 strip.className = Roo.bootstrap.version == 4 ?
41005 "navbar-light bg-light" :
41006 "navbar navbar-default"; //"x-tabs-wrap";
41007 container.appendChild(strip);
41011 createStripList : function(strip)
41013 // div wrapper for retard IE
41014 // returns the "tr" element.
41015 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41016 //'<div class="x-tabs-strip-wrap">'+
41017 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41018 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41019 return strip.firstChild; //.firstChild.firstChild.firstChild;
41021 createBody : function(container)
41023 var body = document.createElement("div");
41024 Roo.id(body, "tab-body");
41025 //Roo.fly(body).addClass("x-tabs-body");
41026 Roo.fly(body).addClass("tab-content");
41027 container.appendChild(body);
41030 createItemBody :function(bodyEl, id){
41031 var body = Roo.getDom(id);
41033 body = document.createElement("div");
41036 //Roo.fly(body).addClass("x-tabs-item-body");
41037 Roo.fly(body).addClass("tab-pane");
41038 bodyEl.insertBefore(body, bodyEl.firstChild);
41042 createStripElements : function(stripEl, text, closable, tpl)
41044 var td = document.createElement("li"); // was td..
41045 td.className = 'nav-item';
41047 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41050 stripEl.appendChild(td);
41052 td.className = "x-tabs-closable";
41053 if(!this.closeTpl){
41054 this.closeTpl = new Roo.Template(
41055 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41056 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41057 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41060 var el = this.closeTpl.overwrite(td, {"text": text});
41061 var close = el.getElementsByTagName("div")[0];
41062 var inner = el.getElementsByTagName("em")[0];
41063 return {"el": el, "close": close, "inner": inner};
41066 // not sure what this is..
41067 // if(!this.tabTpl){
41068 //this.tabTpl = new Roo.Template(
41069 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41070 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41072 // this.tabTpl = new Roo.Template(
41073 // '<a href="#">' +
41074 // '<span unselectable="on"' +
41075 // (this.disableTooltips ? '' : ' title="{text}"') +
41076 // ' >{text}</span></a>'
41082 var template = tpl || this.tabTpl || false;
41085 template = new Roo.Template(
41086 Roo.bootstrap.version == 4 ?
41088 '<a class="nav-link" href="#" unselectable="on"' +
41089 (this.disableTooltips ? '' : ' title="{text}"') +
41092 '<a class="nav-link" href="#">' +
41093 '<span unselectable="on"' +
41094 (this.disableTooltips ? '' : ' title="{text}"') +
41095 ' >{text}</span></a>'
41100 switch (typeof(template)) {
41104 template = new Roo.Template(template);
41110 var el = template.overwrite(td, {"text": text});
41112 var inner = el.getElementsByTagName("span")[0];
41114 return {"el": el, "inner": inner};
41122 * @class Roo.TabPanelItem
41123 * @extends Roo.util.Observable
41124 * Represents an individual item (tab plus body) in a TabPanel.
41125 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41126 * @param {String} id The id of this TabPanelItem
41127 * @param {String} text The text for the tab of this TabPanelItem
41128 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41130 Roo.bootstrap.panel.TabItem = function(config){
41132 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41133 * @type Roo.TabPanel
41135 this.tabPanel = config.panel;
41137 * The id for this TabPanelItem
41140 this.id = config.id;
41142 this.disabled = false;
41144 this.text = config.text;
41146 this.loaded = false;
41147 this.closable = config.closable;
41150 * The body element for this TabPanelItem.
41151 * @type Roo.Element
41153 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41154 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41155 this.bodyEl.setStyle("display", "block");
41156 this.bodyEl.setStyle("zoom", "1");
41157 //this.hideAction();
41159 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41161 this.el = Roo.get(els.el);
41162 this.inner = Roo.get(els.inner, true);
41163 this.textEl = Roo.bootstrap.version == 4 ?
41164 this.el : Roo.get(this.el.dom.firstChild, true);
41166 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41167 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41170 // this.el.on("mousedown", this.onTabMouseDown, this);
41171 this.el.on("click", this.onTabClick, this);
41173 if(config.closable){
41174 var c = Roo.get(els.close, true);
41175 c.dom.title = this.closeText;
41176 c.addClassOnOver("close-over");
41177 c.on("click", this.closeClick, this);
41183 * Fires when this tab becomes the active tab.
41184 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41185 * @param {Roo.TabPanelItem} this
41189 * @event beforeclose
41190 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41191 * @param {Roo.TabPanelItem} this
41192 * @param {Object} e Set cancel to true on this object to cancel the close.
41194 "beforeclose": true,
41197 * Fires when this tab is closed.
41198 * @param {Roo.TabPanelItem} this
41202 * @event deactivate
41203 * Fires when this tab is no longer the active tab.
41204 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41205 * @param {Roo.TabPanelItem} this
41207 "deactivate" : true
41209 this.hidden = false;
41211 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41214 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41216 purgeListeners : function(){
41217 Roo.util.Observable.prototype.purgeListeners.call(this);
41218 this.el.removeAllListeners();
41221 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41224 this.status_node.addClass("active");
41227 this.tabPanel.stripWrap.repaint();
41229 this.fireEvent("activate", this.tabPanel, this);
41233 * Returns true if this tab is the active tab.
41234 * @return {Boolean}
41236 isActive : function(){
41237 return this.tabPanel.getActiveTab() == this;
41241 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41244 this.status_node.removeClass("active");
41246 this.fireEvent("deactivate", this.tabPanel, this);
41249 hideAction : function(){
41250 this.bodyEl.hide();
41251 this.bodyEl.setStyle("position", "absolute");
41252 this.bodyEl.setLeft("-20000px");
41253 this.bodyEl.setTop("-20000px");
41256 showAction : function(){
41257 this.bodyEl.setStyle("position", "relative");
41258 this.bodyEl.setTop("");
41259 this.bodyEl.setLeft("");
41260 this.bodyEl.show();
41264 * Set the tooltip for the tab.
41265 * @param {String} tooltip The tab's tooltip
41267 setTooltip : function(text){
41268 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41269 this.textEl.dom.qtip = text;
41270 this.textEl.dom.removeAttribute('title');
41272 this.textEl.dom.title = text;
41276 onTabClick : function(e){
41277 e.preventDefault();
41278 this.tabPanel.activate(this.id);
41281 onTabMouseDown : function(e){
41282 e.preventDefault();
41283 this.tabPanel.activate(this.id);
41286 getWidth : function(){
41287 return this.inner.getWidth();
41290 setWidth : function(width){
41291 var iwidth = width - this.linode.getPadding("lr");
41292 this.inner.setWidth(iwidth);
41293 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41294 this.linode.setWidth(width);
41298 * Show or hide the tab
41299 * @param {Boolean} hidden True to hide or false to show.
41301 setHidden : function(hidden){
41302 this.hidden = hidden;
41303 this.linode.setStyle("display", hidden ? "none" : "");
41307 * Returns true if this tab is "hidden"
41308 * @return {Boolean}
41310 isHidden : function(){
41311 return this.hidden;
41315 * Returns the text for this tab
41318 getText : function(){
41322 autoSize : function(){
41323 //this.el.beginMeasure();
41324 this.textEl.setWidth(1);
41326 * #2804 [new] Tabs in Roojs
41327 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41329 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41330 //this.el.endMeasure();
41334 * Sets the text for the tab (Note: this also sets the tooltip text)
41335 * @param {String} text The tab's text and tooltip
41337 setText : function(text){
41339 this.textEl.update(text);
41340 this.setTooltip(text);
41341 //if(!this.tabPanel.resizeTabs){
41342 // this.autoSize();
41346 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41348 activate : function(){
41349 this.tabPanel.activate(this.id);
41353 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41355 disable : function(){
41356 if(this.tabPanel.active != this){
41357 this.disabled = true;
41358 this.status_node.addClass("disabled");
41363 * Enables this TabPanelItem if it was previously disabled.
41365 enable : function(){
41366 this.disabled = false;
41367 this.status_node.removeClass("disabled");
41371 * Sets the content for this TabPanelItem.
41372 * @param {String} content The content
41373 * @param {Boolean} loadScripts true to look for and load scripts
41375 setContent : function(content, loadScripts){
41376 this.bodyEl.update(content, loadScripts);
41380 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41381 * @return {Roo.UpdateManager} The UpdateManager
41383 getUpdateManager : function(){
41384 return this.bodyEl.getUpdateManager();
41388 * Set a URL to be used to load the content for this TabPanelItem.
41389 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41390 * @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)
41391 * @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)
41392 * @return {Roo.UpdateManager} The UpdateManager
41394 setUrl : function(url, params, loadOnce){
41395 if(this.refreshDelegate){
41396 this.un('activate', this.refreshDelegate);
41398 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41399 this.on("activate", this.refreshDelegate);
41400 return this.bodyEl.getUpdateManager();
41404 _handleRefresh : function(url, params, loadOnce){
41405 if(!loadOnce || !this.loaded){
41406 var updater = this.bodyEl.getUpdateManager();
41407 updater.update(url, params, this._setLoaded.createDelegate(this));
41412 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41413 * Will fail silently if the setUrl method has not been called.
41414 * This does not activate the panel, just updates its content.
41416 refresh : function(){
41417 if(this.refreshDelegate){
41418 this.loaded = false;
41419 this.refreshDelegate();
41424 _setLoaded : function(){
41425 this.loaded = true;
41429 closeClick : function(e){
41432 this.fireEvent("beforeclose", this, o);
41433 if(o.cancel !== true){
41434 this.tabPanel.removeTab(this.id);
41438 * The text displayed in the tooltip for the close icon.
41441 closeText : "Close this tab"
41444 * This script refer to:
41445 * Title: International Telephone Input
41446 * Author: Jack O'Connor
41447 * Code version: v12.1.12
41448 * Availability: https://github.com/jackocnr/intl-tel-input.git
41451 Roo.bootstrap.PhoneInputData = function() {
41454 "Afghanistan (افغانستان)",
41459 "Albania (Shqipëri)",
41464 "Algeria (الجزائر)",
41489 "Antigua and Barbuda",
41499 "Armenia (Հայաստան)",
41515 "Austria (Österreich)",
41520 "Azerbaijan (Azərbaycan)",
41530 "Bahrain (البحرين)",
41535 "Bangladesh (বাংলাদেশ)",
41545 "Belarus (Беларусь)",
41550 "Belgium (België)",
41580 "Bosnia and Herzegovina (Босна и Херцеговина)",
41595 "British Indian Ocean Territory",
41600 "British Virgin Islands",
41610 "Bulgaria (България)",
41620 "Burundi (Uburundi)",
41625 "Cambodia (កម្ពុជា)",
41630 "Cameroon (Cameroun)",
41639 ["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"]
41642 "Cape Verde (Kabu Verdi)",
41647 "Caribbean Netherlands",
41658 "Central African Republic (République centrafricaine)",
41678 "Christmas Island",
41684 "Cocos (Keeling) Islands",
41695 "Comoros (جزر القمر)",
41700 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41705 "Congo (Republic) (Congo-Brazzaville)",
41725 "Croatia (Hrvatska)",
41746 "Czech Republic (Česká republika)",
41751 "Denmark (Danmark)",
41766 "Dominican Republic (República Dominicana)",
41770 ["809", "829", "849"]
41788 "Equatorial Guinea (Guinea Ecuatorial)",
41808 "Falkland Islands (Islas Malvinas)",
41813 "Faroe Islands (Føroyar)",
41834 "French Guiana (Guyane française)",
41839 "French Polynesia (Polynésie française)",
41854 "Georgia (საქართველო)",
41859 "Germany (Deutschland)",
41879 "Greenland (Kalaallit Nunaat)",
41916 "Guinea-Bissau (Guiné Bissau)",
41941 "Hungary (Magyarország)",
41946 "Iceland (Ísland)",
41966 "Iraq (العراق)",
41982 "Israel (ישראל)",
42009 "Jordan (الأردن)",
42014 "Kazakhstan (Казахстан)",
42035 "Kuwait (الكويت)",
42040 "Kyrgyzstan (Кыргызстан)",
42050 "Latvia (Latvija)",
42055 "Lebanon (لبنان)",
42070 "Libya (ليبيا)",
42080 "Lithuania (Lietuva)",
42095 "Macedonia (FYROM) (Македонија)",
42100 "Madagascar (Madagasikara)",
42130 "Marshall Islands",
42140 "Mauritania (موريتانيا)",
42145 "Mauritius (Moris)",
42166 "Moldova (Republica Moldova)",
42176 "Mongolia (Монгол)",
42181 "Montenegro (Crna Gora)",
42191 "Morocco (المغرب)",
42197 "Mozambique (Moçambique)",
42202 "Myanmar (Burma) (မြန်မာ)",
42207 "Namibia (Namibië)",
42222 "Netherlands (Nederland)",
42227 "New Caledonia (Nouvelle-Calédonie)",
42262 "North Korea (조선 민주주의 인민 공화국)",
42267 "Northern Mariana Islands",
42283 "Pakistan (پاکستان)",
42293 "Palestine (فلسطين)",
42303 "Papua New Guinea",
42345 "Réunion (La Réunion)",
42351 "Romania (România)",
42367 "Saint Barthélemy",
42378 "Saint Kitts and Nevis",
42388 "Saint Martin (Saint-Martin (partie française))",
42394 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42399 "Saint Vincent and the Grenadines",
42414 "São Tomé and Príncipe (São Tomé e Príncipe)",
42419 "Saudi Arabia (المملكة العربية السعودية)",
42424 "Senegal (Sénégal)",
42454 "Slovakia (Slovensko)",
42459 "Slovenia (Slovenija)",
42469 "Somalia (Soomaaliya)",
42479 "South Korea (대한민국)",
42484 "South Sudan (جنوب السودان)",
42494 "Sri Lanka (ශ්රී ලංකාව)",
42499 "Sudan (السودان)",
42509 "Svalbard and Jan Mayen",
42520 "Sweden (Sverige)",
42525 "Switzerland (Schweiz)",
42530 "Syria (سوريا)",
42575 "Trinidad and Tobago",
42580 "Tunisia (تونس)",
42585 "Turkey (Türkiye)",
42595 "Turks and Caicos Islands",
42605 "U.S. Virgin Islands",
42615 "Ukraine (Україна)",
42620 "United Arab Emirates (الإمارات العربية المتحدة)",
42642 "Uzbekistan (Oʻzbekiston)",
42652 "Vatican City (Città del Vaticano)",
42663 "Vietnam (Việt Nam)",
42668 "Wallis and Futuna (Wallis-et-Futuna)",
42673 "Western Sahara (الصحراء الغربية)",
42679 "Yemen (اليمن)",
42703 * This script refer to:
42704 * Title: International Telephone Input
42705 * Author: Jack O'Connor
42706 * Code version: v12.1.12
42707 * Availability: https://github.com/jackocnr/intl-tel-input.git
42711 * @class Roo.bootstrap.PhoneInput
42712 * @extends Roo.bootstrap.TriggerField
42713 * An input with International dial-code selection
42715 * @cfg {String} defaultDialCode default '+852'
42716 * @cfg {Array} preferedCountries default []
42719 * Create a new PhoneInput.
42720 * @param {Object} config Configuration options
42723 Roo.bootstrap.PhoneInput = function(config) {
42724 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42727 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42729 listWidth: undefined,
42731 selectedClass: 'active',
42733 invalidClass : "has-warning",
42735 validClass: 'has-success',
42737 allowed: '0123456789',
42742 * @cfg {String} defaultDialCode The default dial code when initializing the input
42744 defaultDialCode: '+852',
42747 * @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
42749 preferedCountries: false,
42751 getAutoCreate : function()
42753 var data = Roo.bootstrap.PhoneInputData();
42754 var align = this.labelAlign || this.parentLabelAlign();
42757 this.allCountries = [];
42758 this.dialCodeMapping = [];
42760 for (var i = 0; i < data.length; i++) {
42762 this.allCountries[i] = {
42766 priority: c[3] || 0,
42767 areaCodes: c[4] || null
42769 this.dialCodeMapping[c[2]] = {
42772 priority: c[3] || 0,
42773 areaCodes: c[4] || null
42785 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42786 maxlength: this.max_length,
42787 cls : 'form-control tel-input',
42788 autocomplete: 'new-password'
42791 var hiddenInput = {
42794 cls: 'hidden-tel-input'
42798 hiddenInput.name = this.name;
42801 if (this.disabled) {
42802 input.disabled = true;
42805 var flag_container = {
42822 cls: this.hasFeedback ? 'has-feedback' : '',
42828 cls: 'dial-code-holder',
42835 cls: 'roo-select2-container input-group',
42842 if (this.fieldLabel.length) {
42845 tooltip: 'This field is required'
42851 cls: 'control-label',
42857 html: this.fieldLabel
42860 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42866 if(this.indicatorpos == 'right') {
42867 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42874 if(align == 'left') {
42882 if(this.labelWidth > 12){
42883 label.style = "width: " + this.labelWidth + 'px';
42885 if(this.labelWidth < 13 && this.labelmd == 0){
42886 this.labelmd = this.labelWidth;
42888 if(this.labellg > 0){
42889 label.cls += ' col-lg-' + this.labellg;
42890 input.cls += ' col-lg-' + (12 - this.labellg);
42892 if(this.labelmd > 0){
42893 label.cls += ' col-md-' + this.labelmd;
42894 container.cls += ' col-md-' + (12 - this.labelmd);
42896 if(this.labelsm > 0){
42897 label.cls += ' col-sm-' + this.labelsm;
42898 container.cls += ' col-sm-' + (12 - this.labelsm);
42900 if(this.labelxs > 0){
42901 label.cls += ' col-xs-' + this.labelxs;
42902 container.cls += ' col-xs-' + (12 - this.labelxs);
42912 var settings = this;
42914 ['xs','sm','md','lg'].map(function(size){
42915 if (settings[size]) {
42916 cfg.cls += ' col-' + size + '-' + settings[size];
42920 this.store = new Roo.data.Store({
42921 proxy : new Roo.data.MemoryProxy({}),
42922 reader : new Roo.data.JsonReader({
42933 'name' : 'dialCode',
42937 'name' : 'priority',
42941 'name' : 'areaCodes',
42948 if(!this.preferedCountries) {
42949 this.preferedCountries = [
42956 var p = this.preferedCountries.reverse();
42959 for (var i = 0; i < p.length; i++) {
42960 for (var j = 0; j < this.allCountries.length; j++) {
42961 if(this.allCountries[j].iso2 == p[i]) {
42962 var t = this.allCountries[j];
42963 this.allCountries.splice(j,1);
42964 this.allCountries.unshift(t);
42970 this.store.proxy.data = {
42972 data: this.allCountries
42978 initEvents : function()
42981 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42983 this.indicator = this.indicatorEl();
42984 this.flag = this.flagEl();
42985 this.dialCodeHolder = this.dialCodeHolderEl();
42987 this.trigger = this.el.select('div.flag-box',true).first();
42988 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42993 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42994 _this.list.setWidth(lw);
42997 this.list.on('mouseover', this.onViewOver, this);
42998 this.list.on('mousemove', this.onViewMove, this);
42999 this.inputEl().on("keyup", this.onKeyUp, this);
43000 this.inputEl().on("keypress", this.onKeyPress, this);
43002 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43004 this.view = new Roo.View(this.list, this.tpl, {
43005 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43008 this.view.on('click', this.onViewClick, this);
43009 this.setValue(this.defaultDialCode);
43012 onTriggerClick : function(e)
43014 Roo.log('trigger click');
43019 if(this.isExpanded()){
43021 this.hasFocus = false;
43023 this.store.load({});
43024 this.hasFocus = true;
43029 isExpanded : function()
43031 return this.list.isVisible();
43034 collapse : function()
43036 if(!this.isExpanded()){
43040 Roo.get(document).un('mousedown', this.collapseIf, this);
43041 Roo.get(document).un('mousewheel', this.collapseIf, this);
43042 this.fireEvent('collapse', this);
43046 expand : function()
43050 if(this.isExpanded() || !this.hasFocus){
43054 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43055 this.list.setWidth(lw);
43058 this.restrictHeight();
43060 Roo.get(document).on('mousedown', this.collapseIf, this);
43061 Roo.get(document).on('mousewheel', this.collapseIf, this);
43063 this.fireEvent('expand', this);
43066 restrictHeight : function()
43068 this.list.alignTo(this.inputEl(), this.listAlign);
43069 this.list.alignTo(this.inputEl(), this.listAlign);
43072 onViewOver : function(e, t)
43074 if(this.inKeyMode){
43077 var item = this.view.findItemFromChild(t);
43080 var index = this.view.indexOf(item);
43081 this.select(index, false);
43086 onViewClick : function(view, doFocus, el, e)
43088 var index = this.view.getSelectedIndexes()[0];
43090 var r = this.store.getAt(index);
43093 this.onSelect(r, index);
43095 if(doFocus !== false && !this.blockFocus){
43096 this.inputEl().focus();
43100 onViewMove : function(e, t)
43102 this.inKeyMode = false;
43105 select : function(index, scrollIntoView)
43107 this.selectedIndex = index;
43108 this.view.select(index);
43109 if(scrollIntoView !== false){
43110 var el = this.view.getNode(index);
43112 this.list.scrollChildIntoView(el, false);
43117 createList : function()
43119 this.list = Roo.get(document.body).createChild({
43121 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43122 style: 'display:none'
43125 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43128 collapseIf : function(e)
43130 var in_combo = e.within(this.el);
43131 var in_list = e.within(this.list);
43132 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43134 if (in_combo || in_list || is_list) {
43140 onSelect : function(record, index)
43142 if(this.fireEvent('beforeselect', this, record, index) !== false){
43144 this.setFlagClass(record.data.iso2);
43145 this.setDialCode(record.data.dialCode);
43146 this.hasFocus = false;
43148 this.fireEvent('select', this, record, index);
43152 flagEl : function()
43154 var flag = this.el.select('div.flag',true).first();
43161 dialCodeHolderEl : function()
43163 var d = this.el.select('input.dial-code-holder',true).first();
43170 setDialCode : function(v)
43172 this.dialCodeHolder.dom.value = '+'+v;
43175 setFlagClass : function(n)
43177 this.flag.dom.className = 'flag '+n;
43180 getValue : function()
43182 var v = this.inputEl().getValue();
43183 if(this.dialCodeHolder) {
43184 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43189 setValue : function(v)
43191 var d = this.getDialCode(v);
43193 //invalid dial code
43194 if(v.length == 0 || !d || d.length == 0) {
43196 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43197 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43203 this.setFlagClass(this.dialCodeMapping[d].iso2);
43204 this.setDialCode(d);
43205 this.inputEl().dom.value = v.replace('+'+d,'');
43206 this.hiddenEl().dom.value = this.getValue();
43211 getDialCode : function(v)
43215 if (v.length == 0) {
43216 return this.dialCodeHolder.dom.value;
43220 if (v.charAt(0) != "+") {
43223 var numericChars = "";
43224 for (var i = 1; i < v.length; i++) {
43225 var c = v.charAt(i);
43228 if (this.dialCodeMapping[numericChars]) {
43229 dialCode = v.substr(1, i);
43231 if (numericChars.length == 4) {
43241 this.setValue(this.defaultDialCode);
43245 hiddenEl : function()
43247 return this.el.select('input.hidden-tel-input',true).first();
43250 // after setting val
43251 onKeyUp : function(e){
43252 this.setValue(this.getValue());
43255 onKeyPress : function(e){
43256 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43263 * @class Roo.bootstrap.MoneyField
43264 * @extends Roo.bootstrap.ComboBox
43265 * Bootstrap MoneyField class
43268 * Create a new MoneyField.
43269 * @param {Object} config Configuration options
43272 Roo.bootstrap.MoneyField = function(config) {
43274 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43278 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43281 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43283 allowDecimals : true,
43285 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43287 decimalSeparator : ".",
43289 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43291 decimalPrecision : 0,
43293 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43295 allowNegative : true,
43297 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43301 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43303 minValue : Number.NEGATIVE_INFINITY,
43305 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43307 maxValue : Number.MAX_VALUE,
43309 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43311 minText : "The minimum value for this field is {0}",
43313 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43315 maxText : "The maximum value for this field is {0}",
43317 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43318 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43320 nanText : "{0} is not a valid number",
43322 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43326 * @cfg {String} defaults currency of the MoneyField
43327 * value should be in lkey
43329 defaultCurrency : false,
43331 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43333 thousandsDelimiter : false,
43335 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43346 getAutoCreate : function()
43348 var align = this.labelAlign || this.parentLabelAlign();
43360 cls : 'form-control roo-money-amount-input',
43361 autocomplete: 'new-password'
43364 var hiddenInput = {
43368 cls: 'hidden-number-input'
43371 if(this.max_length) {
43372 input.maxlength = this.max_length;
43376 hiddenInput.name = this.name;
43379 if (this.disabled) {
43380 input.disabled = true;
43383 var clg = 12 - this.inputlg;
43384 var cmd = 12 - this.inputmd;
43385 var csm = 12 - this.inputsm;
43386 var cxs = 12 - this.inputxs;
43390 cls : 'row roo-money-field',
43394 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43398 cls: 'roo-select2-container input-group',
43402 cls : 'form-control roo-money-currency-input',
43403 autocomplete: 'new-password',
43405 name : this.currencyName
43409 cls : 'input-group-addon',
43423 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43427 cls: this.hasFeedback ? 'has-feedback' : '',
43438 if (this.fieldLabel.length) {
43441 tooltip: 'This field is required'
43447 cls: 'control-label',
43453 html: this.fieldLabel
43456 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43462 if(this.indicatorpos == 'right') {
43463 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43470 if(align == 'left') {
43478 if(this.labelWidth > 12){
43479 label.style = "width: " + this.labelWidth + 'px';
43481 if(this.labelWidth < 13 && this.labelmd == 0){
43482 this.labelmd = this.labelWidth;
43484 if(this.labellg > 0){
43485 label.cls += ' col-lg-' + this.labellg;
43486 input.cls += ' col-lg-' + (12 - this.labellg);
43488 if(this.labelmd > 0){
43489 label.cls += ' col-md-' + this.labelmd;
43490 container.cls += ' col-md-' + (12 - this.labelmd);
43492 if(this.labelsm > 0){
43493 label.cls += ' col-sm-' + this.labelsm;
43494 container.cls += ' col-sm-' + (12 - this.labelsm);
43496 if(this.labelxs > 0){
43497 label.cls += ' col-xs-' + this.labelxs;
43498 container.cls += ' col-xs-' + (12 - this.labelxs);
43509 var settings = this;
43511 ['xs','sm','md','lg'].map(function(size){
43512 if (settings[size]) {
43513 cfg.cls += ' col-' + size + '-' + settings[size];
43520 initEvents : function()
43522 this.indicator = this.indicatorEl();
43524 this.initCurrencyEvent();
43526 this.initNumberEvent();
43529 initCurrencyEvent : function()
43532 throw "can not find store for combo";
43535 this.store = Roo.factory(this.store, Roo.data);
43536 this.store.parent = this;
43540 this.triggerEl = this.el.select('.input-group-addon', true).first();
43542 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43547 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43548 _this.list.setWidth(lw);
43551 this.list.on('mouseover', this.onViewOver, this);
43552 this.list.on('mousemove', this.onViewMove, this);
43553 this.list.on('scroll', this.onViewScroll, this);
43556 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43559 this.view = new Roo.View(this.list, this.tpl, {
43560 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43563 this.view.on('click', this.onViewClick, this);
43565 this.store.on('beforeload', this.onBeforeLoad, this);
43566 this.store.on('load', this.onLoad, this);
43567 this.store.on('loadexception', this.onLoadException, this);
43569 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43570 "up" : function(e){
43571 this.inKeyMode = true;
43575 "down" : function(e){
43576 if(!this.isExpanded()){
43577 this.onTriggerClick();
43579 this.inKeyMode = true;
43584 "enter" : function(e){
43587 if(this.fireEvent("specialkey", this, e)){
43588 this.onViewClick(false);
43594 "esc" : function(e){
43598 "tab" : function(e){
43601 if(this.fireEvent("specialkey", this, e)){
43602 this.onViewClick(false);
43610 doRelay : function(foo, bar, hname){
43611 if(hname == 'down' || this.scope.isExpanded()){
43612 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43620 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43624 initNumberEvent : function(e)
43626 this.inputEl().on("keydown" , this.fireKey, this);
43627 this.inputEl().on("focus", this.onFocus, this);
43628 this.inputEl().on("blur", this.onBlur, this);
43630 this.inputEl().relayEvent('keyup', this);
43632 if(this.indicator){
43633 this.indicator.addClass('invisible');
43636 this.originalValue = this.getValue();
43638 if(this.validationEvent == 'keyup'){
43639 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43640 this.inputEl().on('keyup', this.filterValidation, this);
43642 else if(this.validationEvent !== false){
43643 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43646 if(this.selectOnFocus){
43647 this.on("focus", this.preFocus, this);
43650 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43651 this.inputEl().on("keypress", this.filterKeys, this);
43653 this.inputEl().relayEvent('keypress', this);
43656 var allowed = "0123456789";
43658 if(this.allowDecimals){
43659 allowed += this.decimalSeparator;
43662 if(this.allowNegative){
43666 if(this.thousandsDelimiter) {
43670 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43672 var keyPress = function(e){
43674 var k = e.getKey();
43676 var c = e.getCharCode();
43679 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43680 allowed.indexOf(String.fromCharCode(c)) === -1
43686 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43690 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43695 this.inputEl().on("keypress", keyPress, this);
43699 onTriggerClick : function(e)
43706 this.loadNext = false;
43708 if(this.isExpanded()){
43713 this.hasFocus = true;
43715 if(this.triggerAction == 'all') {
43716 this.doQuery(this.allQuery, true);
43720 this.doQuery(this.getRawValue());
43723 getCurrency : function()
43725 var v = this.currencyEl().getValue();
43730 restrictHeight : function()
43732 this.list.alignTo(this.currencyEl(), this.listAlign);
43733 this.list.alignTo(this.currencyEl(), this.listAlign);
43736 onViewClick : function(view, doFocus, el, e)
43738 var index = this.view.getSelectedIndexes()[0];
43740 var r = this.store.getAt(index);
43743 this.onSelect(r, index);
43747 onSelect : function(record, index){
43749 if(this.fireEvent('beforeselect', this, record, index) !== false){
43751 this.setFromCurrencyData(index > -1 ? record.data : false);
43755 this.fireEvent('select', this, record, index);
43759 setFromCurrencyData : function(o)
43763 this.lastCurrency = o;
43765 if (this.currencyField) {
43766 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43768 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43771 this.lastSelectionText = currency;
43773 //setting default currency
43774 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43775 this.setCurrency(this.defaultCurrency);
43779 this.setCurrency(currency);
43782 setFromData : function(o)
43786 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43788 this.setFromCurrencyData(c);
43793 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43795 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43798 this.setValue(value);
43802 setCurrency : function(v)
43804 this.currencyValue = v;
43807 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43812 setValue : function(v)
43814 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43820 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43822 this.inputEl().dom.value = (v == '') ? '' :
43823 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43825 if(!this.allowZero && v === '0') {
43826 this.hiddenEl().dom.value = '';
43827 this.inputEl().dom.value = '';
43834 getRawValue : function()
43836 var v = this.inputEl().getValue();
43841 getValue : function()
43843 return this.fixPrecision(this.parseValue(this.getRawValue()));
43846 parseValue : function(value)
43848 if(this.thousandsDelimiter) {
43850 r = new RegExp(",", "g");
43851 value = value.replace(r, "");
43854 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43855 return isNaN(value) ? '' : value;
43859 fixPrecision : function(value)
43861 if(this.thousandsDelimiter) {
43863 r = new RegExp(",", "g");
43864 value = value.replace(r, "");
43867 var nan = isNaN(value);
43869 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43870 return nan ? '' : value;
43872 return parseFloat(value).toFixed(this.decimalPrecision);
43875 decimalPrecisionFcn : function(v)
43877 return Math.floor(v);
43880 validateValue : function(value)
43882 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43886 var num = this.parseValue(value);
43889 this.markInvalid(String.format(this.nanText, value));
43893 if(num < this.minValue){
43894 this.markInvalid(String.format(this.minText, this.minValue));
43898 if(num > this.maxValue){
43899 this.markInvalid(String.format(this.maxText, this.maxValue));
43906 validate : function()
43908 if(this.disabled || this.allowBlank){
43913 var currency = this.getCurrency();
43915 if(this.validateValue(this.getRawValue()) && currency.length){
43920 this.markInvalid();
43924 getName: function()
43929 beforeBlur : function()
43935 var v = this.parseValue(this.getRawValue());
43942 onBlur : function()
43946 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43947 //this.el.removeClass(this.focusClass);
43950 this.hasFocus = false;
43952 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43956 var v = this.getValue();
43958 if(String(v) !== String(this.startValue)){
43959 this.fireEvent('change', this, v, this.startValue);
43962 this.fireEvent("blur", this);
43965 inputEl : function()
43967 return this.el.select('.roo-money-amount-input', true).first();
43970 currencyEl : function()
43972 return this.el.select('.roo-money-currency-input', true).first();
43975 hiddenEl : function()
43977 return this.el.select('input.hidden-number-input',true).first();
43981 * @class Roo.bootstrap.BezierSignature
43982 * @extends Roo.bootstrap.Component
43983 * Bootstrap BezierSignature class
43984 * This script refer to:
43985 * Title: Signature Pad
43987 * Availability: https://github.com/szimek/signature_pad
43990 * Create a new BezierSignature
43991 * @param {Object} config The config object
43994 Roo.bootstrap.BezierSignature = function(config){
43995 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44001 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44008 mouse_btn_down: true,
44011 * @cfg {int} canvas height
44013 canvas_height: '200px',
44016 * @cfg {float|function} Radius of a single dot.
44021 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44026 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44031 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44036 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44041 * @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.
44043 bg_color: 'rgba(0, 0, 0, 0)',
44046 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44048 dot_color: 'black',
44051 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44053 velocity_filter_weight: 0.7,
44056 * @cfg {function} Callback when stroke begin.
44061 * @cfg {function} Callback when stroke end.
44065 getAutoCreate : function()
44067 var cls = 'roo-signature column';
44070 cls += ' ' + this.cls;
44080 for(var i = 0; i < col_sizes.length; i++) {
44081 if(this[col_sizes[i]]) {
44082 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44092 cls: 'roo-signature-body',
44096 cls: 'roo-signature-body-canvas',
44097 height: this.canvas_height,
44098 width: this.canvas_width
44105 style: 'display: none'
44113 initEvents: function()
44115 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44117 var canvas = this.canvasEl();
44119 // mouse && touch event swapping...
44120 canvas.dom.style.touchAction = 'none';
44121 canvas.dom.style.msTouchAction = 'none';
44123 this.mouse_btn_down = false;
44124 canvas.on('mousedown', this._handleMouseDown, this);
44125 canvas.on('mousemove', this._handleMouseMove, this);
44126 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44128 if (window.PointerEvent) {
44129 canvas.on('pointerdown', this._handleMouseDown, this);
44130 canvas.on('pointermove', this._handleMouseMove, this);
44131 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44134 if ('ontouchstart' in window) {
44135 canvas.on('touchstart', this._handleTouchStart, this);
44136 canvas.on('touchmove', this._handleTouchMove, this);
44137 canvas.on('touchend', this._handleTouchEnd, this);
44140 Roo.EventManager.onWindowResize(this.resize, this, true);
44142 // file input event
44143 this.fileEl().on('change', this.uploadImage, this);
44150 resize: function(){
44152 var canvas = this.canvasEl().dom;
44153 var ctx = this.canvasElCtx();
44154 var img_data = false;
44156 if(canvas.width > 0) {
44157 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44159 // setting canvas width will clean img data
44162 var style = window.getComputedStyle ?
44163 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44165 var padding_left = parseInt(style.paddingLeft) || 0;
44166 var padding_right = parseInt(style.paddingRight) || 0;
44168 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44171 ctx.putImageData(img_data, 0, 0);
44175 _handleMouseDown: function(e)
44177 if (e.browserEvent.which === 1) {
44178 this.mouse_btn_down = true;
44179 this.strokeBegin(e);
44183 _handleMouseMove: function (e)
44185 if (this.mouse_btn_down) {
44186 this.strokeMoveUpdate(e);
44190 _handleMouseUp: function (e)
44192 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44193 this.mouse_btn_down = false;
44198 _handleTouchStart: function (e) {
44200 e.preventDefault();
44201 if (e.browserEvent.targetTouches.length === 1) {
44202 // var touch = e.browserEvent.changedTouches[0];
44203 // this.strokeBegin(touch);
44205 this.strokeBegin(e); // assume e catching the correct xy...
44209 _handleTouchMove: function (e) {
44210 e.preventDefault();
44211 // var touch = event.targetTouches[0];
44212 // _this._strokeMoveUpdate(touch);
44213 this.strokeMoveUpdate(e);
44216 _handleTouchEnd: function (e) {
44217 var wasCanvasTouched = e.target === this.canvasEl().dom;
44218 if (wasCanvasTouched) {
44219 e.preventDefault();
44220 // var touch = event.changedTouches[0];
44221 // _this._strokeEnd(touch);
44226 reset: function () {
44227 this._lastPoints = [];
44228 this._lastVelocity = 0;
44229 this._lastWidth = (this.min_width + this.max_width) / 2;
44230 this.canvasElCtx().fillStyle = this.dot_color;
44233 strokeMoveUpdate: function(e)
44235 this.strokeUpdate(e);
44237 if (this.throttle) {
44238 this.throttleStroke(this.strokeUpdate, this.throttle);
44241 this.strokeUpdate(e);
44245 strokeBegin: function(e)
44247 var newPointGroup = {
44248 color: this.dot_color,
44252 if (typeof this.onBegin === 'function') {
44256 this.curve_data.push(newPointGroup);
44258 this.strokeUpdate(e);
44261 strokeUpdate: function(e)
44263 var rect = this.canvasEl().dom.getBoundingClientRect();
44264 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44265 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44266 var lastPoints = lastPointGroup.points;
44267 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44268 var isLastPointTooClose = lastPoint
44269 ? point.distanceTo(lastPoint) <= this.min_distance
44271 var color = lastPointGroup.color;
44272 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44273 var curve = this.addPoint(point);
44275 this.drawDot({color: color, point: point});
44278 this.drawCurve({color: color, curve: curve});
44288 strokeEnd: function(e)
44290 this.strokeUpdate(e);
44291 if (typeof this.onEnd === 'function') {
44296 addPoint: function (point) {
44297 var _lastPoints = this._lastPoints;
44298 _lastPoints.push(point);
44299 if (_lastPoints.length > 2) {
44300 if (_lastPoints.length === 3) {
44301 _lastPoints.unshift(_lastPoints[0]);
44303 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44304 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44305 _lastPoints.shift();
44311 calculateCurveWidths: function (startPoint, endPoint) {
44312 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44313 (1 - this.velocity_filter_weight) * this._lastVelocity;
44315 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44318 start: this._lastWidth
44321 this._lastVelocity = velocity;
44322 this._lastWidth = newWidth;
44326 drawDot: function (_a) {
44327 var color = _a.color, point = _a.point;
44328 var ctx = this.canvasElCtx();
44329 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44331 this.drawCurveSegment(point.x, point.y, width);
44333 ctx.fillStyle = color;
44337 drawCurve: function (_a) {
44338 var color = _a.color, curve = _a.curve;
44339 var ctx = this.canvasElCtx();
44340 var widthDelta = curve.endWidth - curve.startWidth;
44341 var drawSteps = Math.floor(curve.length()) * 2;
44343 ctx.fillStyle = color;
44344 for (var i = 0; i < drawSteps; i += 1) {
44345 var t = i / drawSteps;
44351 var x = uuu * curve.startPoint.x;
44352 x += 3 * uu * t * curve.control1.x;
44353 x += 3 * u * tt * curve.control2.x;
44354 x += ttt * curve.endPoint.x;
44355 var y = uuu * curve.startPoint.y;
44356 y += 3 * uu * t * curve.control1.y;
44357 y += 3 * u * tt * curve.control2.y;
44358 y += ttt * curve.endPoint.y;
44359 var width = curve.startWidth + ttt * widthDelta;
44360 this.drawCurveSegment(x, y, width);
44366 drawCurveSegment: function (x, y, width) {
44367 var ctx = this.canvasElCtx();
44369 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44370 this.is_empty = false;
44375 var ctx = this.canvasElCtx();
44376 var canvas = this.canvasEl().dom;
44377 ctx.fillStyle = this.bg_color;
44378 ctx.clearRect(0, 0, canvas.width, canvas.height);
44379 ctx.fillRect(0, 0, canvas.width, canvas.height);
44380 this.curve_data = [];
44382 this.is_empty = true;
44387 return this.el.select('input',true).first();
44390 canvasEl: function()
44392 return this.el.select('canvas',true).first();
44395 canvasElCtx: function()
44397 return this.el.select('canvas',true).first().dom.getContext('2d');
44400 getImage: function(type)
44402 if(this.is_empty) {
44407 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44410 drawFromImage: function(img_src)
44412 var img = new Image();
44414 img.onload = function(){
44415 this.canvasElCtx().drawImage(img, 0, 0);
44420 this.is_empty = false;
44423 selectImage: function()
44425 this.fileEl().dom.click();
44428 uploadImage: function(e)
44430 var reader = new FileReader();
44432 reader.onload = function(e){
44433 var img = new Image();
44434 img.onload = function(){
44436 this.canvasElCtx().drawImage(img, 0, 0);
44438 img.src = e.target.result;
44441 reader.readAsDataURL(e.target.files[0]);
44444 // Bezier Point Constructor
44445 Point: (function () {
44446 function Point(x, y, time) {
44449 this.time = time || Date.now();
44451 Point.prototype.distanceTo = function (start) {
44452 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44454 Point.prototype.equals = function (other) {
44455 return this.x === other.x && this.y === other.y && this.time === other.time;
44457 Point.prototype.velocityFrom = function (start) {
44458 return this.time !== start.time
44459 ? this.distanceTo(start) / (this.time - start.time)
44466 // Bezier Constructor
44467 Bezier: (function () {
44468 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44469 this.startPoint = startPoint;
44470 this.control2 = control2;
44471 this.control1 = control1;
44472 this.endPoint = endPoint;
44473 this.startWidth = startWidth;
44474 this.endWidth = endWidth;
44476 Bezier.fromPoints = function (points, widths, scope) {
44477 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44478 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44479 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44481 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44482 var dx1 = s1.x - s2.x;
44483 var dy1 = s1.y - s2.y;
44484 var dx2 = s2.x - s3.x;
44485 var dy2 = s2.y - s3.y;
44486 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44487 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44488 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44489 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44490 var dxm = m1.x - m2.x;
44491 var dym = m1.y - m2.y;
44492 var k = l2 / (l1 + l2);
44493 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44494 var tx = s2.x - cm.x;
44495 var ty = s2.y - cm.y;
44497 c1: new scope.Point(m1.x + tx, m1.y + ty),
44498 c2: new scope.Point(m2.x + tx, m2.y + ty)
44501 Bezier.prototype.length = function () {
44506 for (var i = 0; i <= steps; i += 1) {
44508 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44509 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44511 var xdiff = cx - px;
44512 var ydiff = cy - py;
44513 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44520 Bezier.prototype.point = function (t, start, c1, c2, end) {
44521 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44522 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44523 + (3.0 * c2 * (1.0 - t) * t * t)
44524 + (end * t * t * t);
44529 throttleStroke: function(fn, wait) {
44530 if (wait === void 0) { wait = 250; }
44532 var timeout = null;
44536 var later = function () {
44537 previous = Date.now();
44539 result = fn.apply(storedContext, storedArgs);
44541 storedContext = null;
44545 return function wrapper() {
44547 for (var _i = 0; _i < arguments.length; _i++) {
44548 args[_i] = arguments[_i];
44550 var now = Date.now();
44551 var remaining = wait - (now - previous);
44552 storedContext = this;
44554 if (remaining <= 0 || remaining > wait) {
44556 clearTimeout(timeout);
44560 result = fn.apply(storedContext, storedArgs);
44562 storedContext = null;
44566 else if (!timeout) {
44567 timeout = window.setTimeout(later, remaining);